# Makefile for GitHub Actions repository # Provides organized task management with parallel execution capabilities .PHONY: help all docs lint format check clean install-tools test test-unit test-integration test-coverage generate-tests generate-tests-dry test-generate-tests docker-build docker-push docker-test docker-login docker-all .DEFAULT_GOAL := help # Colors for output GREEN := $(shell printf '\033[32m') YELLOW := $(shell printf '\033[33m') RED := $(shell printf '\033[31m') BLUE := $(shell printf '\033[34m') RESET := $(shell printf '\033[0m') # Configuration SHELL := /bin/bash .SHELLFLAGS := -euo pipefail -c # Log file with timestamp LOG_FILE := update_$(shell date +%Y%m%d_%H%M%S).log # Detect OS for sed compatibility UNAME_S := $(shell uname -s) ifeq ($(UNAME_S),Darwin) SED_CMD := sed -i .bak else SED_CMD := sed -i endif # Help target - shows available commands help: ## Show this help message @echo "$(BLUE)GitHub Actions Repository Management$(RESET)" @echo "" @echo "$(GREEN)Available targets:$(RESET)" @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \ awk 'BEGIN {FS = ":.*?## "}; {printf " $(YELLOW)%-20s$(RESET) %s\n", $$1, $$2}' @echo "" @echo "$(GREEN)Examples:$(RESET)" @echo " make all # Generate docs, format, and lint everything" @echo " make docs # Generate documentation only" @echo " make lint # Run all linters" @echo " make format # Format all files" @echo " make test # Run all tests (unit + integration)" @echo " make check # Quick syntax checks" # Main targets all: install-tools update-validators docs format lint precommit ## Generate docs, format, lint, and run pre-commit @echo "$(GREEN)โœ… All tasks completed successfully$(RESET)" docs: ## Generate documentation for all actions @echo "$(BLUE)๐Ÿ“‚ Generating documentation...$(RESET)" @failed=0; \ for dir in $$(find . -mindepth 2 -maxdepth 2 -name "action.yml" | sed 's|/action.yml||' | sed 's|./||'); do \ echo "$(BLUE)๐Ÿ“„ Updating $$dir/README.md...$(RESET)"; \ repo="ivuorinen/actions/$$dir"; \ printf "# %s\n\n" "$$repo" > "$$dir/README.md"; \ if npx --yes action-docs -n -s "$$dir/action.yml" --no-banner >> "$$dir/README.md" 2>/dev/null; then \ $(SED_CMD) "s|\*\*\*PROJECT\*\*\*|$$repo|g" "$$dir/README.md"; \ $(SED_CMD) "s|\*\*\*VERSION\*\*\*|main|g" "$$dir/README.md"; \ $(SED_CMD) "s|\*\*\*||g" "$$dir/README.md"; \ [ "$(UNAME_S)" = "Darwin" ] && rm -f "$$dir/README.md.bak"; \ echo "$(GREEN)โœ… Updated $$dir/README.md$(RESET)"; \ else \ echo "$(RED)โš ๏ธ Failed to update $$dir/README.md$(RESET)" | tee -a $(LOG_FILE); \ failed=$$((failed + 1)); \ fi; \ done; \ [ $$failed -eq 0 ] && echo "$(GREEN)โœ… All documentation updated successfully$(RESET)" || { echo "$(RED)โŒ $$failed documentation updates failed$(RESET)"; exit 1; } update-validators: ## Update validation rules for all actions @echo "$(BLUE)๐Ÿ”ง Updating validation rules...$(RESET)" @if command -v uv >/dev/null 2>&1; then \ cd validate-inputs && uv run scripts/update-validators.py; \ else \ echo "$(RED)โŒ uv not found. Please install uv (see 'make install-tools')$(RESET)"; \ exit 1; \ fi @echo "$(GREEN)โœ… Validation rules updated$(RESET)" update-validators-dry: ## Preview validation rules changes (dry run) @echo "$(BLUE)๐Ÿ” Previewing validation rules changes...$(RESET)" @if command -v uv >/dev/null 2>&1; then \ cd validate-inputs && uv run scripts/update-validators.py --dry-run; \ else \ echo "$(RED)โŒ uv not found. Please install uv (see 'make install-tools')$(RESET)"; \ exit 1; \ fi format: format-markdown format-yaml-json format-python ## Format all files @echo "$(GREEN)โœ… All files formatted$(RESET)" lint: lint-markdown lint-yaml lint-shell lint-python ## Run all linters @echo "$(GREEN)โœ… All linting completed$(RESET)" check: check-tools check-syntax check-local-refs ## Quick syntax and tool availability checks @echo "$(GREEN)โœ… All checks passed$(RESET)" clean: ## Clean up temporary files and caches @echo "$(BLUE)๐Ÿงน Cleaning up...$(RESET)" @find . -name "*.bak" -delete 2>/dev/null || true @find . -name "update_*.log" -mtime +7 -delete 2>/dev/null || true @find . -name ".megalinter" -type d -exec rm -rf {} + 2>/dev/null || true @echo "$(GREEN)โœ… Cleanup completed$(RESET)" precommit: ## Run pre-commit hooks on all files @echo "$(BLUE)๐Ÿ” Running pre-commit hooks...$(RESET)" @if command -v pre-commit >/dev/null 2>&1; then \ if PRE_COMMIT_USE_UV=1 pre-commit run --all-files; then \ echo "$(GREEN)โœ… All pre-commit hooks passed$(RESET)"; \ else \ echo "$(RED)โŒ Some pre-commit hooks failed$(RESET)"; \ exit 1; \ fi; \ else \ echo "$(RED)โŒ pre-commit not found. Please install:$(RESET)"; \ echo " brew install pre-commit"; \ echo " or: pip install pre-commit"; \ exit 1; \ fi # Local action reference validation check-local-refs: ## Check for ../action-name references that should be ./action-name @echo "$(BLUE)๐Ÿ” Checking local action references...$(RESET)" @if command -v uv >/dev/null 2>&1; then \ uv run _tools/fix-local-action-refs.py --check; \ else \ echo "$(RED)โŒ uv not found. Please install uv (see 'make install-tools')$(RESET)"; \ exit 1; \ fi fix-local-refs: ## Fix ../action-name references to ./action-name @echo "$(BLUE)๐Ÿ”ง Fixing local action references...$(RESET)" @if command -v uv >/dev/null 2>&1; then \ uv run _tools/fix-local-action-refs.py; \ else \ echo "$(RED)โŒ uv not found. Please install uv (see 'make install-tools')$(RESET)"; \ exit 1; \ fi fix-local-refs-dry: ## Preview local action reference fixes (dry run) @echo "$(BLUE)๐Ÿ” Previewing local action reference fixes...$(RESET)" @if command -v uv >/dev/null 2>&1; then \ uv run _tools/fix-local-action-refs.py --dry-run; \ else \ echo "$(RED)โŒ uv not found. Please install uv (see 'make install-tools')$(RESET)"; \ exit 1; \ fi # Formatting targets format-markdown: ## Format markdown files @echo "$(BLUE)๐Ÿ“ Formatting markdown...$(RESET)" @if npx --yes markdownlint-cli2 --fix "**/*.md" "#node_modules" 2>/dev/null; then \ echo "$(GREEN)โœ… Markdown formatted$(RESET)"; \ else \ echo "$(YELLOW)โš ๏ธ Markdown formatting issues found$(RESET)" | tee -a $(LOG_FILE); \ fi format-yaml-json: ## Format YAML and JSON files @echo "$(BLUE)โœจ Formatting YAML/JSON...$(RESET)" @if command -v yamlfmt >/dev/null 2>&1; then \ if yamlfmt . 2>/dev/null; then \ echo "$(GREEN)โœ… YAML formatted with yamlfmt$(RESET)"; \ else \ echo "$(YELLOW)โš ๏ธ YAML formatting issues found with yamlfmt$(RESET)" | tee -a $(LOG_FILE); \ fi; \ else \ echo "$(BLUE)โ„น๏ธ yamlfmt not available, skipping$(RESET)"; \ fi @if npx --yes prettier --write "**/*.md" "**/*.yml" "**/*.yaml" "**/*.json" 2>/dev/null; then \ echo "$(GREEN)โœ… YAML/JSON formatted with prettier$(RESET)"; \ else \ echo "$(YELLOW)โš ๏ธ YAML/JSON formatting issues found with prettier$(RESET)" | tee -a $(LOG_FILE); \ fi @echo "$(BLUE)๐Ÿ“Š Formatting tables...$(RESET)" @if npx --yes markdown-table-formatter "**/*.md" 2>/dev/null; then \ echo "$(GREEN)โœ… Tables formatted$(RESET)"; \ else \ echo "$(YELLOW)โš ๏ธ Table formatting issues found$(RESET)" | tee -a $(LOG_FILE); \ fi format-tables: ## Format markdown tables @echo "$(BLUE)๐Ÿ“Š Formatting tables...$(RESET)" @if npx --yes markdown-table-formatter "**/*.md" 2>/dev/null; then \ echo "$(GREEN)โœ… Tables formatted$(RESET)"; \ else \ echo "$(YELLOW)โš ๏ธ Table formatting issues found$(RESET)" | tee -a $(LOG_FILE); \ fi format-python: ## Format Python files with ruff @echo "$(BLUE)๐Ÿ Formatting Python files...$(RESET)" @if command -v uv >/dev/null 2>&1; then \ if uvx ruff format . --no-cache; then \ echo "$(GREEN)โœ… Python files formatted$(RESET)"; \ else \ echo "$(YELLOW)โš ๏ธ Python formatting issues found$(RESET)" | tee -a $(LOG_FILE); \ fi; \ else \ echo "$(BLUE)โ„น๏ธ uv not available, skipping Python formatting$(RESET)"; \ fi # Linting targets lint-markdown: ## Lint markdown files @echo "$(BLUE)๐Ÿ” Linting markdown...$(RESET)" @if npx --yes markdownlint-cli2 --fix "**/*.md" "#node_modules"; then \ echo "$(GREEN)โœ… Markdown linting passed$(RESET)"; \ else \ echo "$(YELLOW)โš ๏ธ Markdown linting issues found$(RESET)" | tee -a $(LOG_FILE); \ fi lint-yaml: ## Lint YAML files @echo "$(BLUE)๐Ÿ” Linting YAML...$(RESET)" @if npx --yes yaml-lint "**/*.yml" "**/*.yaml" 2>/dev/null; then \ echo "$(GREEN)โœ… YAML linting passed$(RESET)"; \ else \ echo "$(YELLOW)โš ๏ธ YAML linting issues found$(RESET)" | tee -a $(LOG_FILE); \ fi lint-shell: ## Lint shell scripts @echo "$(BLUE)๐Ÿ” Linting shell scripts...$(RESET)" @if command -v shellcheck >/dev/null 2>&1; then \ if find . -name "*.sh" -not -path "./_tests/*" -exec shellcheck -x {} + 2>/dev/null; then \ echo "$(GREEN)โœ… Shell linting passed$(RESET)"; \ else \ echo "$(YELLOW)โš ๏ธ Shell linting issues found$(RESET)" | tee -a $(LOG_FILE); \ fi; \ else \ echo "$(BLUE)โ„น๏ธ shellcheck not available, skipping shell script linting$(RESET)"; \ fi lint-python: ## Lint Python files with ruff and pyright @echo "$(BLUE)๐Ÿ” Linting Python files...$(RESET)" @ruff_passed=true; pyright_passed=true; \ if command -v uv >/dev/null 2>&1; then \ uvx ruff check --fix . --no-cache; \ if ! uvx ruff check . --no-cache; then \ echo "$(YELLOW)โš ๏ธ Python linting issues found$(RESET)" | tee -a $(LOG_FILE); \ ruff_passed=false; \ fi; \ if command -v pyright >/dev/null 2>&1; then \ if ! pyright --pythonpath $$(which python3) validate-inputs/ _tests/framework/; then \ echo "$(YELLOW)โš ๏ธ Python type checking issues found$(RESET)" | tee -a $(LOG_FILE); \ pyright_passed=false; \ fi; \ else \ echo "$(BLUE)โ„น๏ธ pyright not available, skipping type checking$(RESET)"; \ fi; \ else \ echo "$(BLUE)โ„น๏ธ uv not available, skipping Python linting$(RESET)"; \ fi; \ if $$ruff_passed && $$pyright_passed; then \ echo "$(GREEN)โœ… Python linting and type checking passed$(RESET)"; \ fi # Check targets check-tools: ## Check if required tools are available @echo "$(BLUE)๐Ÿ”ง Checking required tools...$(RESET)" @for cmd in npx sed find grep shellcheck; do \ if ! command -v $$cmd >/dev/null 2>&1; then \ echo "$(RED)โŒ Error: $$cmd not found$(RESET)"; \ echo " Please install $$cmd (see 'make install-tools')"; \ exit 1; \ fi; \ done @if ! command -v yamlfmt >/dev/null 2>&1; then \ echo "$(YELLOW)โš ๏ธ yamlfmt not found (optional for YAML formatting)$(RESET)"; \ fi @echo "$(GREEN)โœ… All required tools available$(RESET)" check-syntax: ## Check syntax of shell scripts and YAML files @echo "$(BLUE)๐Ÿ” Checking syntax...$(RESET)" @failed=0; \ find . -name "*.sh" -print0 | while IFS= read -r -d '' file; do \ if ! bash -n "$$file" 2>&1; then \ echo "$(RED)โŒ Syntax error in $$file$(RESET)" >&2; \ failed=1; \ fi; \ done; \ if [ "$$failed" -eq 1 ]; then \ echo "$(RED)โŒ Shell script syntax errors found$(RESET)"; \ exit 1; \ fi @echo "$(GREEN)โœ… Syntax checks passed$(RESET)" install-tools: ## Install/update required tools @echo "$(BLUE)๐Ÿ“ฆ Installing/updating tools...$(RESET)" @echo "$(YELLOW)Installing NPM tools...$(RESET)" @npx --yes action-docs@latest --version >/dev/null @npx --yes markdownlint-cli2 --version >/dev/null @npx --yes prettier --version >/dev/null @npx --yes markdown-table-formatter --version >/dev/null @npx --yes yaml-lint --version >/dev/null @echo "$(YELLOW)Checking shellcheck...$(RESET)" @if ! command -v shellcheck >/dev/null 2>&1; then \ echo "$(RED)โš ๏ธ shellcheck not found. Please install:$(RESET)"; \ echo " macOS: brew install shellcheck"; \ echo " Linux: apt-get install shellcheck"; \ else \ echo " shellcheck already installed"; \ fi @echo "$(YELLOW)Checking yamlfmt...$(RESET)" @if ! command -v yamlfmt >/dev/null 2>&1; then \ echo "$(RED)โš ๏ธ yamlfmt not found. Please install:$(RESET)"; \ echo " macOS: brew install yamlfmt"; \ echo " Linux: go install github.com/google/yamlfmt/cmd/yamlfmt@latest"; \ else \ echo " yamlfmt already installed"; \ fi @echo "$(YELLOW)Checking uv...$(RESET)" @if ! command -v uv >/dev/null 2>&1; then \ echo "$(RED)โš ๏ธ uv not found. Please install:$(RESET)"; \ echo " macOS: brew install uv"; \ echo " Linux: curl -LsSf https://astral.sh/uv/install.sh | sh"; \ echo " Or see: https://docs.astral.sh/uv/getting-started/installation/"; \ exit 1; \ else \ echo " uv already installed"; \ fi @echo "$(YELLOW)Checking pre-commit...$(RESET)" @if ! command -v pre-commit >/dev/null 2>&1; then \ echo "$(BLUE)โ„น๏ธ pre-commit not found. Installing via uv tool...$(RESET)"; \ uv tool install pre-commit; \ echo " pre-commit installed"; \ else \ echo " pre-commit already installed"; \ fi @echo "$(YELLOW)Installing git hooks with pre-commit...$(RESET)" @if [ -d .git ] && command -v pre-commit >/dev/null 2>&1; then \ if ~/.local/bin/pre-commit install 2>/dev/null || pre-commit install 2>/dev/null; then \ echo " Git hooks installed"; \ fi; \ fi @echo "$(YELLOW)Installing Python dependencies from pyproject.toml...$(RESET)" @uv sync --all-extras @echo " Python dependencies installed" @echo "$(GREEN)โœ… All tools installed/updated$(RESET)" # Development targets dev: ## Development workflow - format then lint @$(MAKE) format @$(MAKE) lint dev-python: ## Python development workflow - format, lint, test @echo "$(BLUE)๐Ÿ Running Python development workflow...$(RESET)" @$(MAKE) format-python @$(MAKE) lint-python @$(MAKE) test-python ci: check docs lint ## CI workflow - check, docs, lint (no formatting) @echo "$(GREEN)โœ… CI workflow completed$(RESET)" # Statistics stats: ## Show repository statistics @echo "$(BLUE)๐Ÿ“Š Repository Statistics$(RESET)" @printf "%-20s %6s\n" "Actions:" "$(shell find . -mindepth 2 -maxdepth 2 -name "action.yml" | wc -l | tr -d ' ')" @printf "%-20s %6s\n" "Shell scripts:" "$(shell find . -name "*.sh" | wc -l | tr -d ' ')" @printf "%-20s %6s\n" "YAML files:" "$(shell find . -name "*.yml" -o -name "*.yaml" | wc -l | tr -d ' ')" @printf "%-20s %6s\n" "Markdown files:" "$(shell find . -name "*.md" | wc -l | tr -d ' ')" @printf "%-20s %6s\n" "Total files:" "$(shell find . -type f | wc -l | tr -d ' ')" # Watch mode for development # Testing targets test: test-python test-update-validators test-actions ## Run all tests (Python + Update validators + GitHub Actions) @echo "$(GREEN)โœ… All tests completed$(RESET)" test-actions: ## Run GitHub Actions tests (unit + integration) @echo "$(BLUE)๐Ÿงช Running GitHub Actions tests...$(RESET)" @if ./_tests/run-tests.sh --type all --format console; then \ echo "$(GREEN)โœ… All GitHub Actions tests passed$(RESET)"; \ else \ echo "$(RED)โŒ Some GitHub Actions tests failed$(RESET)"; \ exit 1; \ fi test-python: ## Run Python validation tests @echo "$(BLUE)๐Ÿ Running Python tests...$(RESET)" @if command -v uv >/dev/null 2>&1; then \ if uv run pytest -v --tb=short; then \ echo "$(GREEN)โœ… Python tests passed$(RESET)"; \ else \ echo "$(RED)โŒ Python tests failed$(RESET)"; \ exit 1; \ fi; \ else \ echo "$(BLUE)โ„น๏ธ uv not available, skipping Python tests$(RESET)"; \ fi test-python-coverage: ## Run Python tests with coverage @echo "$(BLUE)๐Ÿ“Š Running Python tests with coverage...$(RESET)" @if command -v uv >/dev/null 2>&1; then \ uv run pytest --cov=validate-inputs --cov-report=term-missing; \ else \ echo "$(BLUE)โ„น๏ธ uv not available, skipping Python coverage tests$(RESET)"; \ fi test-update-validators: ## Run tests for update-validators.py script @echo "$(BLUE)๐Ÿ”ง Running update-validators.py tests...$(RESET)" @if command -v uv >/dev/null 2>&1; then \ if uv run pytest validate-inputs/tests/test_update_validators.py -v --tb=short; then \ echo "$(GREEN)โœ… Update-validators tests passed$(RESET)"; \ else \ echo "$(RED)โŒ Update-validators tests failed$(RESET)"; \ exit 1; \ fi; \ else \ echo "$(BLUE)โ„น๏ธ uv not available, skipping update-validators tests$(RESET)"; \ fi test-unit: ## Run unit tests only @echo "$(BLUE)๐Ÿ”ฌ Running unit tests...$(RESET)" @./_tests/run-tests.sh --type unit --format console test-integration: ## Run integration tests only @echo "$(BLUE)๐Ÿ”— Running integration tests...$(RESET)" @./_tests/run-tests.sh --type integration --format console test-coverage: ## Run tests with coverage reporting @echo "$(BLUE)๐Ÿ“Š Running tests with coverage...$(RESET)" @./_tests/run-tests.sh --type all --coverage --format console test-action: ## Run tests for specific action (usage: make test-action ACTION=node-setup) @if [ -z "$(ACTION)" ]; then \ echo "$(RED)โŒ Error: ACTION parameter required$(RESET)"; \ echo "Usage: make test-action ACTION=node-setup"; \ exit 1; \ fi @echo "$(BLUE)๐ŸŽฏ Running tests for action: $(ACTION)$(RESET)" @./_tests/run-tests.sh --action $(ACTION) --format console generate-tests: ## Generate missing tests for actions and validators (won't overwrite existing tests) @echo "$(BLUE)๐Ÿงช Generating missing tests...$(RESET)" @if command -v uv >/dev/null 2>&1; then \ if uv run validate-inputs/scripts/generate-tests.py; then \ echo "$(GREEN)โœ… Test generation completed$(RESET)"; \ else \ echo "$(RED)โŒ Test generation failed$(RESET)"; \ exit 1; \ fi; \ else \ echo "$(RED)โŒ uv not found. Please install uv (see 'make install-tools')$(RESET)"; \ exit 1; \ fi generate-tests-dry: ## Preview what tests would be generated without creating files @echo "$(BLUE)๐Ÿ‘๏ธ Preview test generation (dry run)...$(RESET)" @if command -v uv >/dev/null 2>&1; then \ uv run validate-inputs/scripts/generate-tests.py --dry-run --verbose; \ else \ echo "$(RED)โŒ uv not found. Please install uv (see 'make install-tools')$(RESET)"; \ exit 1; \ fi test-generate-tests: ## Test the test generation system itself @echo "$(BLUE)๐Ÿ”ฌ Testing test generation system...$(RESET)" @if command -v uv >/dev/null 2>&1; then \ if uv run pytest validate-inputs/tests/test_generate_tests.py -v; then \ echo "$(GREEN)โœ… Test generation tests passed$(RESET)"; \ else \ echo "$(RED)โŒ Test generation tests failed$(RESET)"; \ exit 1; \ fi; \ else \ echo "$(RED)โŒ uv not found. Please install uv (see 'make install-tools')$(RESET)"; \ exit 1; \ fi # Docker targets docker-build: ## Build the testing-tools Docker image @echo "$(BLUE)๐Ÿณ Building testing-tools Docker image...$(RESET)" @if ! command -v docker >/dev/null 2>&1; then \ echo "$(RED)โŒ Docker not found. Please install Docker.$(RESET)"; \ exit 1; \ fi @if bash _tools/docker-testing-tools/build.sh; then \ echo "$(GREEN)โœ… Docker image built successfully$(RESET)"; \ else \ echo "$(RED)โŒ Docker build failed$(RESET)"; \ exit 1; \ fi docker-test: ## Test the Docker image locally @echo "$(BLUE)๐Ÿงช Testing Docker image...$(RESET)" @if ! command -v docker >/dev/null 2>&1; then \ echo "$(RED)โŒ Docker not found$(RESET)"; \ exit 1; \ fi @echo "$(BLUE)Testing basic functionality...$(RESET)" @docker run --rm ghcr.io/ivuorinen/actions:testing-tools whoami @docker run --rm ghcr.io/ivuorinen/actions:testing-tools shellspec --version @docker run --rm ghcr.io/ivuorinen/actions:testing-tools act --version @echo "$(GREEN)โœ… Docker image tests passed$(RESET)" docker-login: ## Authenticate with GitHub Container Registry @echo "$(BLUE)๐Ÿ” Authenticating with ghcr.io...$(RESET)" @TOKEN=""; \ TOKEN_SOURCE=""; \ if [ -n "$${GITHUB_TOKEN-}" ]; then \ echo "$(BLUE)Using GITHUB_TOKEN from environment$(RESET)"; \ TOKEN="$${GITHUB_TOKEN}"; \ TOKEN_SOURCE="env"; \ elif command -v gh >/dev/null 2>&1 && gh auth status >/dev/null 2>&1; then \ echo "$(BLUE)Using token from GitHub CLI (gh)$(RESET)"; \ TOKEN=$$(gh auth token); \ TOKEN_SOURCE="gh"; \ else \ echo "$(RED)โŒ No authentication method available$(RESET)"; \ echo ""; \ echo "$(YELLOW)To authenticate with ghcr.io, you need a token with 'write:packages' scope$(RESET)"; \ echo ""; \ echo "$(GREEN)Option 1: Use environment variable$(RESET)"; \ echo " export GITHUB_TOKEN=ghp_xxxxxxxxxxxx"; \ echo " make docker-login"; \ echo ""; \ echo "$(GREEN)Option 2: Use GitHub CLI with proper scopes$(RESET)"; \ echo " gh auth login --scopes 'write:packages'"; \ echo " make docker-login"; \ echo ""; \ echo "$(GREEN)Option 3: Create a Personal Access Token$(RESET)"; \ echo " 1. Go to: https://github.com/settings/tokens/new"; \ echo " 2. Check: write:packages (includes read:packages)"; \ echo " 3. Generate token and use with Option 1"; \ exit 1; \ fi; \ if printf '%s' "$${TOKEN}" | docker login ghcr.io -u ivuorinen --password-stdin 2>&1 | tee /tmp/docker-login.log | grep -q "Login Succeeded"; then \ echo "$(GREEN)โœ… Successfully authenticated with ghcr.io$(RESET)"; \ rm -f /tmp/docker-login.log; \ else \ echo "$(RED)โŒ Authentication failed$(RESET)"; \ echo ""; \ if grep -q "scope" /tmp/docker-login.log 2>/dev/null; then \ echo "$(YELLOW)โš ๏ธ Token does not have required 'write:packages' scope$(RESET)"; \ echo ""; \ if [ "$$TOKEN_SOURCE" = "gh" ]; then \ echo "$(BLUE)GitHub CLI tokens need package permissions.$(RESET)"; \ echo ""; \ if [ -n "$${GITHUB_TOKEN-}" ]; then \ echo "$(YELLOW)Note: GITHUB_TOKEN is set in your environment, which prevents gh auth refresh.$(RESET)"; \ echo "Clear it first, then refresh:"; \ echo ""; \ echo "$(GREEN)For Fish shell:$(RESET)"; \ echo " set -e GITHUB_TOKEN"; \ echo " gh auth refresh --scopes 'write:packages'"; \ echo ""; \ echo "$(GREEN)For Bash/Zsh:$(RESET)"; \ echo " unset GITHUB_TOKEN"; \ echo " gh auth refresh --scopes 'write:packages'"; \ else \ echo "Run:"; \ echo " gh auth refresh --scopes 'write:packages'"; \ fi; \ echo ""; \ echo "Then try again:"; \ echo " make docker-login"; \ else \ echo "Your GITHUB_TOKEN needs 'write:packages' scope."; \ echo ""; \ echo "$(GREEN)Create a new token:$(RESET)"; \ echo " 1. Go to: https://github.com/settings/tokens/new"; \ echo " 2. Check: write:packages (includes read:packages)"; \ echo " 3. Generate and copy the token"; \ echo ""; \ echo "$(GREEN)For Fish shell:$(RESET)"; \ echo " set -gx GITHUB_TOKEN ghp_xxxxxxxxxxxx"; \ echo ""; \ echo "$(GREEN)For Bash/Zsh:$(RESET)"; \ echo " export GITHUB_TOKEN=ghp_xxxxxxxxxxxx"; \ fi; \ fi; \ rm -f /tmp/docker-login.log; \ exit 1; \ fi docker-push: ## Push the testing-tools image to ghcr.io @echo "$(BLUE)๐Ÿ“ค Pushing Docker image to ghcr.io...$(RESET)" @if ! command -v docker >/dev/null 2>&1; then \ echo "$(RED)โŒ Docker not found$(RESET)"; \ exit 1; \ fi @if ! docker images ghcr.io/ivuorinen/actions:testing-tools -q | grep -q .; then \ echo "$(RED)โŒ Image not found. Run 'make docker-build' first$(RESET)"; \ exit 1; \ fi @PUSH_OUTPUT=$$(docker push ghcr.io/ivuorinen/actions:testing-tools 2>&1); \ PUSH_EXIT=$$?; \ echo "$${PUSH_OUTPUT}"; \ if [ $$PUSH_EXIT -ne 0 ]; then \ echo ""; \ if echo "$${PUSH_OUTPUT}" | grep -q "scope"; then \ echo "$(RED)โŒ Token does not have required 'write:packages' scope$(RESET)"; \ echo ""; \ echo "$(YELLOW)Fix the authentication:$(RESET)"; \ echo ""; \ if [ -n "$${GITHUB_TOKEN-}" ]; then \ echo "$(BLUE)Option 1: Clear GITHUB_TOKEN and use gh auth$(RESET)"; \ echo ""; \ echo "For Fish shell:"; \ echo " set -e GITHUB_TOKEN"; \ echo " gh auth refresh --scopes 'write:packages'"; \ echo " make docker-push"; \ echo ""; \ echo "For Bash/Zsh:"; \ echo " unset GITHUB_TOKEN"; \ echo " gh auth refresh --scopes 'write:packages'"; \ echo " make docker-push"; \ echo ""; \ echo "$(BLUE)Option 2: Create a new token with write:packages scope$(RESET)"; \ else \ echo "$(BLUE)Option 1: Use GitHub CLI$(RESET)"; \ echo " gh auth refresh --scopes 'write:packages'"; \ echo " make docker-push"; \ echo ""; \ echo "$(BLUE)Option 2: Use Personal Access Token$(RESET)"; \ fi; \ echo " 1. Go to: https://github.com/settings/tokens/new"; \ echo " 2. Check: write:packages"; \ echo " 3. Generate and copy token"; \ echo ""; \ echo " For Fish shell:"; \ echo " set -gx GITHUB_TOKEN ghp_xxxxxxxxxxxx"; \ echo " make docker-push"; \ echo ""; \ echo " For Bash/Zsh:"; \ echo " export GITHUB_TOKEN=ghp_xxxxxxxxxxxx"; \ echo " make docker-push"; \ exit 1; \ elif echo "$${PUSH_OUTPUT}" | grep -q "denied\|unauthorized"; then \ echo "$(YELLOW)โš ๏ธ Authentication required. Attempting login...$(RESET)"; \ if $(MAKE) docker-login; then \ echo ""; \ echo "$(BLUE)Retrying push...$(RESET)"; \ if ! docker push ghcr.io/ivuorinen/actions:testing-tools; then \ echo "$(RED)โŒ Retry push failed$(RESET)"; \ exit 1; \ fi; \ else \ exit 1; \ fi; \ else \ echo "$(RED)โŒ Push failed$(RESET)"; \ exit 1; \ fi; \ fi @echo "$(GREEN)โœ… Image pushed successfully$(RESET)" @echo "" @echo "Image available at:" @echo " ghcr.io/ivuorinen/actions:testing-tools" docker-all: docker-build docker-test docker-push ## Build, test, and push Docker image @echo "$(GREEN)โœ… All Docker operations completed$(RESET)" watch: ## Watch files and auto-format on changes (requires entr) @if command -v entr >/dev/null 2>&1; then \ echo "$(BLUE)๐Ÿ‘€ Watching for changes... (press Ctrl+C to stop)$(RESET)"; \ find . -name "*.yml" -o -name "*.yaml" -o -name "*.md" -o -name "*.sh" | \ entr -c $(MAKE) format; \ else \ echo "$(RED)โŒ Error: entr not found. Install with: brew install entr$(RESET)"; \ exit 1; \ fi