mirror of
https://github.com/ivuorinen/actions.git
synced 2026-01-26 03:23:59 +00:00
* feat: first pass simplification
* refactor: simplify actions repository structure
Major simplification reducing actions from 44 to 30:
Consolidations:
- Merge biome-check + biome-fix → biome-lint (mode: check/fix)
- Merge eslint-check + eslint-fix → eslint-lint (mode: check/fix)
- Merge prettier-check + prettier-fix → prettier-lint (mode: check/fix)
- Merge 5 version-detect actions → language-version-detect (language param)
Removals:
- common-file-check, common-retry (better served by external tools)
- docker-publish-gh, docker-publish-hub (consolidated into docker-publish)
- github-release (redundant with existing tooling)
- set-git-config (no longer needed)
- version-validator (functionality moved to language-version-detect)
Fixes:
- Rewrite docker-publish to use official Docker actions directly
- Update validate-inputs example (eslint-fix → eslint-lint)
- Update tests and documentation for new structure
Result: ~6,000 lines removed, cleaner action catalog, maintained functionality.
* refactor: complete action simplification and cleanup
Remove deprecated actions and update remaining actions:
Removed:
- common-file-check, common-retry: utility actions
- docker-publish-gh, docker-publish-hub: replaced by docker-publish wrapper
- github-release, version-validator, set-git-config: no longer needed
- Various version-detect actions: replaced by language-version-detect
Updated:
- docker-publish: rewrite as simple wrapper using official Docker actions
- validate-inputs: update example (eslint-fix → eslint-lint)
- Multiple actions: update configurations and remove deprecated dependencies
- Tests: update integration/unit tests for new structure
- Documentation: update README, remove test for deleted actions
Configuration updates:
- Linter configs, ignore files for new structure
- Makefile, pyproject.toml updates
* fix: enforce POSIX compliance in GitHub workflows
Convert all workflow shell scripts to POSIX-compliant sh:
Critical fixes:
- Replace bash with sh in all shell declarations
- Replace [[ with [ for test conditions
- Replace == with = for string comparisons
- Replace set -euo pipefail with set -eu
- Split compound AND conditions into separate [ ] tests
Files updated:
- .github/workflows/test-actions.yml (7 shell declarations, 10 test operators)
- .github/workflows/security-suite.yml (set -eu)
- .github/workflows/action-security.yml (2 shell declarations)
- .github/workflows/pr-lint.yml (3 shell declarations)
- .github/workflows/issue-stats.yml (1 shell declaration)
Ensures compatibility with minimal sh implementations and aligns with
CLAUDE.md standards requiring POSIX shell compliance across all scripts.
All tests pass: 764 pytest tests, 100% coverage.
* fix: add missing permissions for private repository support
Add critical permissions to pr-lint workflow for private repositories:
Workflow-level permissions:
+ packages: read - Access private npm/PyPI/Composer packages
Job-level permissions:
+ packages: read - Access private packages during dependency installation
+ checks: write - Create and update check runs
Fixes failures when:
- Installing private npm packages from GitHub Packages
- Installing private Composer dependencies
- Installing private Python packages
- Creating status checks with github-script
Valid permission scopes per actionlint:
actions, attestations, checks, contents, deployments, discussions,
id-token, issues, models, packages, pages, pull-requests,
repository-projects, security-events, statuses
Note: "workflows" and "metadata" are NOT valid permission scopes
(they are PAT-only scopes or auto-granted respectively).
* docs: update readmes
* fix: replace bash-specific 'source' with POSIX '.' command
Replace all occurrences of 'source' with '.' (dot) for POSIX compliance:
Changes in python-lint-fix/action.yml:
- Line 165: source .venv/bin/activate → . .venv/bin/activate
- Line 179: source .venv/bin/activate → . .venv/bin/activate
- Line 211: source .venv/bin/activate → . .venv/bin/activate
Also fixed bash-specific test operator:
- Line 192: [[ "$FAIL_ON_ERROR" == "true" ]] → [ "$FAIL_ON_ERROR" = "true" ]
The 'source' command is bash-specific. POSIX sh uses '.' (dot) to source files.
Both commands have identical functionality but '.' is portable across all
POSIX-compliant shells.
* security: fix code injection vulnerability in docker-publish
Fix CodeQL code injection warning (CWE-094, CWE-095, CWE-116):
Issue: inputs.context was used directly in GitHub Actions expression
without sanitization at line 194, allowing potential code injection
by external users.
Fix: Use environment variable indirection to prevent expression injection:
- Added env.BUILD_CONTEXT to capture inputs.context
- Changed context parameter to use ${{ env.BUILD_CONTEXT }}
Environment variables are evaluated after expression compilation,
preventing malicious code execution during workflow parsing.
Security Impact: Medium severity (CVSS 5.0)
Identified by: GitHub Advanced Security (CodeQL)
Reference: https://github.com/ivuorinen/actions/pull/353#pullrequestreview-3481935924
* security: prevent credential persistence in pr-lint checkout
Add persist-credentials: false to checkout step to mitigate untrusted
checkout vulnerability. This prevents GITHUB_TOKEN from being accessible
to potentially malicious PR code.
Fixes: CodeQL finding CWE-829 (untrusted checkout on privileged workflow)
* fix: prevent security bot from overwriting unrelated comments
Replace broad string matching with unique HTML comment marker for
identifying bot-generated comments. Previously, any comment containing
'Security Analysis' or '🔐 GitHub Actions Permissions' would be
overwritten, causing data loss.
Changes:
- Add unique marker: <!-- security-analysis-bot-comment -->
- Prepend marker to generated comment body
- Update comment identification to use marker only
- Add defensive null check for comment.body
This fixes critical data loss bug where user comments could be
permanently overwritten by the security analysis bot.
Follows same proven pattern as test-actions.yml coverage comments.
* improve: show concise permissions diff instead of full blocks
Replace verbose full-block permissions diff with line-by-line changes.
Now shows only added/removed permissions, making output much more
readable.
Changes:
- Parse permissions into individual lines
- Compare old vs new to identify actual changes
- Show only removed (-) and added (+) lines in diff
- Collapse unchanged permissions into details section (≤3 items)
- Show count summary for many unchanged permissions (>3 items)
Example output:
Before: 30+ lines showing entire permissions block
After: 3-5 lines showing only what changed
This addresses user feedback that permissions changes were too verbose.
* security: add input validation and trust model documentation
Add comprehensive security validation for docker-publish action to prevent
code injection attacks (CWE-094, CWE-116).
Changes:
- Add validation for context input (reject absolute paths, warn on URLs)
- Add validation for dockerfile input (reject absolute/URL paths)
- Document security trust model in README
- Add best practices for secure usage
- Explain validation rules and threat model
Prevents malicious actors from:
- Building from arbitrary file system locations
- Fetching Dockerfiles from untrusted remote sources
- Executing code injection through build context manipulation
Addresses: CodeRabbit review comments #2541434325, #2541549615
Fixes: GitHub Advanced Security code injection findings
* security: replace unmaintained nick-fields/retry with step-security/retry
Replace nick-fields/retry with step-security/retry across all 4 actions:
- csharp-build/action.yml
- php-composer/action.yml
- go-build/action.yml
- ansible-lint-fix/action.yml
The nick-fields/retry action has security vulnerabilities and low maintenance.
step-security/retry is a drop-in replacement with full API compatibility.
All inputs (timeout_minutes, max_attempts, command, retry_wait_seconds) are
compatible. Using SHA-pinned version for security.
Addresses CodeRabbit review comment #2541549598
* test: add is_input_required() helper function
Add helper function to check if an action input is required, reducing
duplication across test suites.
The function:
- Takes action_file and input_name as parameters
- Uses validation_core.py to query the 'required' property
- Returns 0 (success) if input is required
- Returns 1 (failure) if input is optional
This DRY improvement addresses CodeRabbit review comment #2541549572
* feat: add mode validation convention mapping
Add "mode" to the validation conventions mapping for lint actions
(eslint-lint, biome-lint, prettier-lint).
Note: The update-validators script doesn't currently recognize "string"
as a validator type, so mode validation coverage remains at 93%. The
actions already have inline validation for mode (check|fix), so this is
primarily for improving coverage metrics.
Addresses part of CodeRabbit review comment #2541549570
(validation coverage improvement)
* docs: fix CLAUDE.md action counts and add missing action
- Update action count from 31 to 29 (line 42)
- Add missing 'action-versioning' to Utilities category (line 74)
Addresses CodeRabbit review comments #2541553130 and #2541553110
* docs: add security considerations to docker-publish
Add security documentation to both action.yml header and README.md:
- Trust model explanation
- Input validation details for context and dockerfile
- Attack prevention information
- Best practices for secure usage
The documentation was previously removed when README was autogenerated.
Now documented in both places to ensure it persists.
* fix: correct step ID reference in docker-build
Fix incorrect step ID reference in platforms output:
- Changed steps.platforms.outputs.built to steps.detect-platforms.outputs.platforms
- The step is actually named 'detect-platforms' not 'platforms'
- Ensures output correctly references the detect-platforms step defined at line 188
* fix: ensure docker-build platforms output is always available
Make detect-platforms step unconditional to fix broken output contract.
The platforms output (line 123) references steps.detect-platforms.outputs.platforms,
but the step only ran when auto-detect-platforms was true (default: false).
This caused undefined output in most cases.
Changes:
- Remove 'if' condition from detect-platforms step
- Step now always runs and always produces platforms output
- When auto-detect is false: outputs configured architectures
- When auto-detect is true: outputs detected platforms or falls back to architectures
- Add '|| true' to grep to prevent errors when no platforms detected
Fixes CodeRabbit review comment #2541824904
* security: remove env var indirection in docker-publish BUILD_CONTEXT
Remove BUILD_CONTEXT env var indirection to address GitHub Advanced Security alert.
The inputs.context is validated at lines 137-147 (rejects absolute paths, warns on URLs)
before being used, so the env var indirection is unnecessary and triggers false positive
code injection warnings.
Changes:
- Remove BUILD_CONTEXT env var (line 254)
- Use inputs.context directly (line 256 → 254)
- Input validation remains in place (lines 137-147)
Fixes GitHub Advanced Security code injection alerts (comments #2541405269, #2541522320)
* feat: implement mode_enum validator for lint actions
Add mode_enum validator to validate mode inputs in linting actions.
Changes to conventions.py:
- Add 'mode_enum' to exact_matches mapping (line 215)
- Add 'mode_enum' to PHP-specific validators list (line 560)
- Implement _validate_mode_enum() method (lines 642-660)
- Validates mode values against ['check', 'fix']
- Returns clear error messages for invalid values
Updated rules.yml files:
- biome-lint: Add mode: mode_enum convention
- eslint-lint: Add mode: mode_enum convention
- prettier-lint: Add mode: mode_enum convention
- All rules.yml: Fix YAML formatting with yamlfmt
This addresses PR #353 comment #2541522326 which reported that mode validation
was being skipped due to unrecognized 'string' type, reducing coverage to 93%.
Tested with biome-lint action - correctly rejects invalid values and accepts
valid 'check' and 'fix' values.
* docs: update action count from 29 to 30 in CLAUDE.md
Update two references to action count in CLAUDE.md:
- Line 42: repository_overview memory description
- Line 74: Repository Structure section header
The repository has 30 actions total (29 listed + validate-inputs).
Addresses PR #353 comment #2541549588.
* docs: use pinned version ref in language-version-detect README
Change usage example from @main to @v2025 for security best practices.
Using pinned version refs (instead of @main) ensures:
- Predictable behavior across workflow runs
- Protection against breaking changes
- Better security through immutable references
Follows repository convention documented in main README and CLAUDE.md.
Addresses PR #353 comment #2541549588.
* refactor: remove deprecated add-snippets input from codeql-analysis
Remove add-snippets input which has been deprecated by GitHub's CodeQL action
and no longer has any effect.
Changes:
- Remove add-snippets input definition (lines 93-96)
- Remove reference in init step (line 129)
- Remove reference in analyze step (line 211)
- Regenerate README and rules.yml
This is a non-breaking change since:
- Default was 'false' (minimal usage expected)
- GitHub's action already ignores this parameter
- Aligns with recent repository simplification efforts
* feat: add mode_enum validator and update rules
Add mode_enum validator support for lint actions and regenerate all validation rules:
Validator Changes:
- Add mode_enum to action_overrides for biome-lint, eslint-lint, prettier-lint
- Remove deprecated add-snippets from codeql-analysis overrides
Rules Updates:
- All 29 action rules.yml files regenerated with consistent YAML formatting
- biome-lint, eslint-lint, prettier-lint now validate mode input (check/fix)
- Improved coverage for lint actions (79% → 83% for biome, 93% for eslint, 79% for prettier)
Documentation:
- Fix language-version-detect README to use @v2025 (not @main)
- Remove outdated docker-publish security docs (now handled by official actions)
This completes PR #353 review feedback implementation.
* fix: replace bash-specific $'\n' with POSIX-compliant printf
Replace non-POSIX $'\n' syntax in tag building loop with printf-based
approach that works in any POSIX shell.
Changed:
- Line 216: tags="${tags}"$'\n'"${image}:${tag}"
+ Line 216: tags="$(printf '%s\n%s' "$tags" "${image}:${tag}")"
This ensures docker-publish/action.yml runs correctly on systems using
/bin/sh instead of bash.
731 lines
29 KiB
Makefile
731 lines
29 KiB
Makefile
# Makefile for GitHub Actions repository
|
||
# Provides organized task management with parallel execution capabilities
|
||
|
||
.PHONY: help all docs update-catalog 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 release release-dry release-prep release-tag release-undo update-version-refs bump-major-version check-version-refs
|
||
.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 update-catalog 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-catalog: ## Update action catalog in README.md
|
||
@echo "$(BLUE)📚 Updating action catalog...$(RESET)"
|
||
@if command -v npm >/dev/null 2>&1; then \
|
||
npm run update-catalog; \
|
||
else \
|
||
echo "$(RED)❌ npm not found. Please install Node.js$(RESET)"; \
|
||
exit 1; \
|
||
fi
|
||
@echo "$(GREEN)✅ Action catalog updated$(RESET)"
|
||
|
||
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
|
||
|
||
# Version management targets
|
||
release: ## Create a new release with version tags (usage: make release [VERSION=v2025.10.18])
|
||
@VERSION_TO_USE=$$(if [ -n "$(VERSION)" ]; then echo "$(VERSION)"; else date +v%Y.%m.%d; fi); \
|
||
echo "$(BLUE)🚀 Creating release $$VERSION_TO_USE...$(RESET)"; \
|
||
sh _tools/release.sh "$$VERSION_TO_USE"
|
||
|
||
release-dry: ## Preview release without making changes (usage: make release-dry VERSION=v2025.11.01)
|
||
@if [ -z "$(VERSION)" ]; then \
|
||
VERSION_TO_USE=$$(date +v%Y.%m.%d); \
|
||
else \
|
||
VERSION_TO_USE="$(VERSION)"; \
|
||
fi; \
|
||
echo "$(BLUE)🔍 Previewing release $$VERSION_TO_USE (dry run)...$(RESET)"; \
|
||
sh _tools/release.sh --dry-run "$$VERSION_TO_USE"
|
||
|
||
release-prep: ## Update action refs and commit (no tags) (usage: make release-prep [VERSION=v2025.11.01])
|
||
@VERSION_TO_USE=$$(if [ -n "$(VERSION)" ]; then echo "$(VERSION)"; else date +v%Y.%m.%d; fi); \
|
||
echo "$(BLUE)🔧 Preparing release $$VERSION_TO_USE...$(RESET)"; \
|
||
sh _tools/release.sh --prep-only "$$VERSION_TO_USE"; \
|
||
echo "$(GREEN)✅ Preparation complete$(RESET)"; \
|
||
echo "$(YELLOW)Next: make release-tag VERSION=$$VERSION_TO_USE$(RESET)"
|
||
|
||
release-tag: ## Create tags only (assumes prep done) (usage: make release-tag VERSION=v2025.11.01)
|
||
@if [ -z "$(VERSION)" ]; then \
|
||
echo "$(RED)❌ Error: VERSION parameter required for release-tag$(RESET)"; \
|
||
echo "Usage: make release-tag VERSION=v2025.11.01"; \
|
||
exit 1; \
|
||
fi; \
|
||
echo "$(BLUE)🏷️ Creating tags for release $(VERSION)...$(RESET)"; \
|
||
sh _tools/release.sh --tag-only "$(VERSION)"
|
||
|
||
release-undo: ## Rollback the most recent release (delete tags and reset HEAD)
|
||
@echo "$(BLUE)🔙 Rolling back release...$(RESET)"; \
|
||
sh _tools/release-undo.sh
|
||
|
||
update-version-refs: ## Update all action references to a specific version tag (usage: make update-version-refs MAJOR=v2025)
|
||
@if [ -z "$(MAJOR)" ]; then \
|
||
echo "$(RED)❌ Error: MAJOR parameter required$(RESET)"; \
|
||
echo "Usage: make update-version-refs MAJOR=v2025"; \
|
||
exit 1; \
|
||
fi
|
||
@echo "$(BLUE)🔧 Updating action references to $(MAJOR)...$(RESET)"
|
||
@sh _tools/update-action-refs.sh "$(MAJOR)"
|
||
@echo "$(GREEN)✅ Action references updated$(RESET)"
|
||
|
||
bump-major-version: ## Replace one major version with another (usage: make bump-major-version OLD=v2025 NEW=v2026)
|
||
@if [ -z "$(OLD)" ] || [ -z "$(NEW)" ]; then \
|
||
echo "$(RED)❌ Error: OLD and NEW parameters required$(RESET)"; \
|
||
echo "Usage: make bump-major-version OLD=v2025 NEW=v2026"; \
|
||
exit 1; \
|
||
fi
|
||
@echo "$(BLUE)🔄 Bumping version from $(OLD) to $(NEW)...$(RESET)"
|
||
@sh _tools/bump-major-version.sh "$(OLD)" "$(NEW)"
|
||
@echo "$(GREEN)✅ Major version bumped$(RESET)"
|
||
|
||
check-version-refs: ## List all current SHA-pinned action references
|
||
@echo "$(BLUE)🔍 Checking action references...$(RESET)"
|
||
@sh _tools/check-version-refs.sh
|
||
|
||
# Formatting targets
|
||
format-markdown: ## Format markdown files
|
||
@echo "$(BLUE)📝 Formatting markdown...$(RESET)"
|
||
@if npx --yes markdownlint-cli2 --fix "**/*.md" "#node_modules" "#.worktrees" 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" "#.worktrees"; 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 \
|
||
echo "$(RED)❌ shellcheck not found. Please install shellcheck:$(RESET)"; \
|
||
echo " brew install shellcheck"; \
|
||
echo " or: apt-get install shellcheck"; \
|
||
exit 1; \
|
||
fi
|
||
@if find . -name "*.sh" -not -path "./_tests/*" -not -path "./.worktrees/*" -exec shellcheck -x {} +; then \
|
||
echo "$(GREEN)✅ Shell linting passed$(RESET)"; \
|
||
else \
|
||
echo "$(RED)❌ Shell linting issues found$(RESET)"; \
|
||
exit 1; \
|
||
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" -not -path "./_tests/*" -not -path "./.worktrees/*" -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" \) \
|
||
-not -path "./_tests/*" -not -path "./.worktrees/*" -not -path "./node_modules/*" | \
|
||
entr -c $(MAKE) format; \
|
||
else \
|
||
echo "$(RED)❌ Error: entr not found. Install with: brew install entr$(RESET)"; \
|
||
exit 1; \
|
||
fi
|