feat: fixes, tweaks, new actions, linting (#186)

* feat: fixes, tweaks, new actions, linting
* fix: improve docker publish loops and dotnet parsing (#193)
* fix: harden action scripts and version checks (#191)
* refactor: major repository restructuring and security enhancements

Add comprehensive development infrastructure:
- Add Makefile with automated documentation generation, formatting, and linting tasks
- Add TODO.md tracking self-containment progress and repository improvements
- Add .nvmrc for consistent Node.js version management
- Create python-version-detect-v2 action for enhanced Python detection

Enhance all GitHub Actions with standardized patterns:
- Add consistent token handling across 27 actions using standardized input patterns
- Implement bash error handling (set -euo pipefail) in all shell steps
- Add comprehensive input validation for path traversal and command injection protection
- Standardize checkout token authentication to prevent rate limiting
- Remove relative action dependencies to ensure external usability

Rewrite security workflow for PR-focused analysis:
- Transform security-suite.yml to PR-only security analysis workflow
- Remove scheduled runs, repository issue management, and Slack notifications
- Implement smart comment generation showing only sections with content
- Add GitHub Actions permission diff analysis and new action detection
- Integrate OWASP, Semgrep, and TruffleHog for comprehensive PR security scanning

Improve version detection and dependency management:
- Simplify version detection actions to use inline logic instead of shared utilities
- Fix Makefile version detection fallback to properly return 'main' when version not found
- Update all external action references to use SHA-pinned versions
- Remove deprecated run.sh in favor of Makefile automation

Update documentation and project standards:
- Enhance CLAUDE.md with self-containment requirements and linting standards
- Update README.md with improved action descriptions and usage examples
- Standardize code formatting with updated .editorconfig and .prettierrc.yml
- Improve GitHub templates for issues and security reporting

This refactoring ensures all 40 actions are fully self-contained and can be used independently when
referenced as ivuorinen/actions/action-name@main, addressing the critical requirement for external
usability while maintaining comprehensive security analysis and development automation.

* feat: add automated action catalog generation system

- Create generate_listing.cjs script for comprehensive action catalog
- Add package.json with development tooling and npm scripts
- Implement automated README.md catalog section with --update flag
- Generate markdown reference-style links for all 40 actions
- Add categorized tables with features, language support matrices
- Replace static reference links with auto-generated dynamic links
- Enable complete automation of action documentation maintenance

* feat: enhance actions with improved documentation and functionality

- Add comprehensive README files for 12 actions with usage examples
- Implement new utility actions (go-version-detect, dotnet-version-detect)
- Enhance node-setup with extensive configuration options
- Improve error handling and validation across all actions
- Update package.json scripts for better development workflow
- Expand TODO.md with detailed roadmap and improvement plans
- Standardize action structure with consistent inputs/outputs

* feat: add comprehensive output handling across all actions

- Add standardized outputs to 15 actions that previously had none
- Implement consistent snake_case naming convention for all outputs
- Add build status and test results outputs to build actions
- Add files changed and status outputs to lint/fix actions
- Add test execution metrics to php-tests action
- Add stale/closed counts to stale action
- Add release URLs and IDs to github-release action
- Update documentation with output specifications
- Mark comprehensive output handling task as complete in TODO.md

* feat: implement shared cache strategy across all actions

- Add caching to 10 actions that previously had none (Node.js, .NET, Python, Go)
- Standardize 4 existing actions to use common-cache instead of direct actions/cache
- Implement consistent cache-hit optimization to skip installations when cache available
- Add language-specific cache configurations with appropriate key files
- Create unified caching approach using ivuorinen/actions/common-cache@main
- Fix YAML syntax error in php-composer action paths parameter
- Update TODO.md to mark shared cache strategy as complete

* feat: implement comprehensive retry logic for network operations

- Create new common-retry action for standardized retry patterns with configurable strategies
- Add retry logic to 9 actions missing network retry capabilities
- Implement exponential backoff, custom timeouts, and flexible error handling
- Add max-retries input parameter to all network-dependent actions (Node.js, .NET, Python, Go)
- Standardize existing retry implementations to use common-retry utility
- Update action catalog to include new common-retry action (41 total actions)
- Update documentation with retry configuration examples and parameters
- Mark retry logic implementation as complete in TODO.md roadmap

* feat: enhance Node.js support with Corepack and Bun

- Add Corepack support for automatic package manager version management
- Add Bun package manager support across all Node.js actions
- Improve Yarn Berry/PnP support with .yarnrc.yml detection
- Add Node.js feature detection (ESM, TypeScript, frameworks)
- Update package manager detection priority and lockfile support
- Enhance caching with package-manager-specific keys
- Update eslint, prettier, and biome actions for multi-package-manager support

* fix: resolve critical runtime issues across multiple actions

- Fix token validation by removing ineffective literal string comparisons
- Add missing @microsoft/eslint-formatter-sarif dependency for SARIF output
- Fix Bash variable syntax errors in username and changelog length checks
- Update Dockerfile version regex to handle tags with suffixes (e.g., -alpine)
- Simplify version selection logic with single grep command
- Fix command execution in retry action with proper bash -c wrapper
- Correct step output references using .outcome instead of .outputs.outcome
- Add missing step IDs for version detection actions
- Include go.mod in cache key files for accurate invalidation
- Require minor version in all version regex patterns
- Improve Bun installation security by verifying script before execution
- Replace bc with sort -V for portable PHP version comparison
- Remove non-existent pre-commit output references

These fixes ensure proper runtime behavior, improved security, and better
cross-platform compatibility across all affected actions.

* fix: resolve critical runtime and security issues across actions

- Fix biome-fix files_changed calculation using git diff instead of git status delta
- Fix compress-images output description and add absolute path validation
- Remove csharp-publish token default and fix token fallback in push commands
- Add @microsoft/eslint-formatter-sarif to all package managers in eslint-check
- Fix eslint-check command syntax by using variable assignment
- Improve node-setup Bun installation security and remove invalid frozen-lockfile flag
- Fix pre-commit token validation by removing ineffective literal comparison
- Fix prettier-fix token comparison and expand regex for all GitHub token types
- Add version-file-parser regex validation safety and fix csproj wildcard handling

These fixes address security vulnerabilities, runtime errors, and functional issues
to ensure reliable operation across all affected GitHub Actions.

* feat: enhance Docker actions with advanced multi-architecture support

Major enhancement to Docker build and publish actions with comprehensive
multi-architecture capabilities and enterprise-grade features.

Added features:
- Advanced buildx configuration (version control, cache modes, build contexts)
- Auto-detect platforms for dynamic architecture discovery
- Performance optimizations with enhanced caching strategies
- Security scanning with Trivy and image signing with Cosign
- SBOM generation in multiple formats with validation
- Verbose logging and dry-run modes for debugging
- Platform-specific build args and fallback mechanisms

Enhanced all Docker actions:
- docker-build: Core buildx features and multi-arch support
- docker-publish-gh: GitHub Packages with security features
- docker-publish-hub: Docker Hub with scanning and signing
- docker-publish: Orchestrator with unified configuration

Updated documentation across all modified actions.

* fix: resolve documentation generation placeholder issue

Fixed Makefile and package.json to properly replace placeholder tokens in generated documentation, ensuring all README files show correct repository paths instead of ***PROJECT***@***VERSION***.

* chore: simplify github token validation
* chore(lint): optional yamlfmt, config and fixes
* feat: use relative `uses` names

* feat: comprehensive testing infrastructure and Python validation system

- Migrate from tests/ to _tests/ directory structure with ShellSpec framework
- Add comprehensive validation system with Python-based input validation
- Implement dual testing approach (ShellSpec + pytest) for complete coverage
- Add modern Python tooling (uv, ruff, pytest-cov) and dependencies
- Create centralized validation rules with automatic generation system
- Update project configuration and build system for new architecture
- Enhance documentation to reflect current testing capabilities

This establishes a robust foundation for action validation and testing
with extensive coverage across all GitHub Actions in the repository.

* chore: remove Dockerfile for now
* chore: code review fixes

* feat: comprehensive GitHub Actions restructuring and tooling improvements

This commit represents a major restructuring of the GitHub Actions monorepo
with improved tooling, testing infrastructure, and comprehensive PR #186
review implementation.

## Major Changes

### 🔧 Development Tooling & Configuration
- **Shellcheck integration**: Exclude shellspec test files from linting
  - Updated .pre-commit-config.yaml to exclude _tests/*.sh from shellcheck/shfmt
  - Modified Makefile shellcheck pattern to skip shellspec files
  - Updated CLAUDE.md documentation with proper exclusion syntax
- **Testing infrastructure**: Enhanced Python validation framework
  - Fixed nested if statements and boolean parameter issues in validation.py
  - Improved code quality with explicit keyword arguments
  - All pre-commit hooks now passing

### 🏗️ Project Structure & Documentation
- **Added Serena AI integration** with comprehensive project memories:
  - Project overview, structure, and technical stack documentation
  - Code style conventions and completion requirements
  - Comprehensive PR #186 review analysis and implementation tracking
- **Enhanced configuration**: Updated .gitignore, .yamlfmt.yml, pyproject.toml
- **Improved testing**: Added integration workflows and enhanced test specs

### 🚀 GitHub Actions Improvements (30+ actions updated)
- **Centralized validation**: Updated 41 validation rule files
- **Enhanced actions**: Improvements across all action categories:
  - Setup actions (node-setup, version detectors)
  - Utility actions (version-file-parser, version-validator)
  - Linting actions (biome, eslint, terraform-lint-fix major refactor)
  - Build/publish actions (docker-build, npm-publish, csharp-*)
  - Repository management actions

### 📝 Documentation Updates
- **README consistency**: Updated version references across action READMEs
- **Enhanced documentation**: Improved action descriptions and usage examples
- **CLAUDE.md**: Updated with current tooling and best practices

## Technical Improvements
- **Security enhancements**: Input validation and sanitization improvements
- **Performance optimizations**: Streamlined action logic and dependencies
- **Cross-platform compatibility**: Better Windows/macOS/Linux support
- **Error handling**: Improved error reporting and user feedback

## Files Changed
- 100 files changed
- 13 new Serena memory files documenting project state
- 41 validation rules updated for consistency
- 30+ GitHub Actions and READMEs improved
- Core tooling configuration enhanced

* feat: comprehensive GitHub Actions improvements and PR review fixes

Major Infrastructure Improvements:
- Add comprehensive testing framework with 17+ ShellSpec validation tests
- Implement Docker-based testing tools with automated test runner
- Add CodeRabbit configuration for automated code reviews
- Restructure documentation and memory management system
- Update validation rules for 25+ actions with enhanced input validation
- Modernize CI/CD workflows and testing infrastructure

Critical PR Review Fixes (All Issues Resolved):
- Fix double caching in node-setup (eliminate redundant cache operations)
- Optimize shell pipeline in version-file-parser (single awk vs complex pipeline)
- Fix GitHub expression interpolation in prettier-check cache keys
- Resolve terraform command order issue (validation after setup)
- Add missing flake8-sarif dependency for Python SARIF output
- Fix environment variable scope in pr-lint (export to GITHUB_ENV)

Performance & Reliability:
- Eliminate duplicate cache operations saving CI time
- Improve shell script efficiency with optimized parsing
- Fix command execution dependencies preventing runtime failures
- Ensure proper dependency installation for all linting tools
- Resolve workflow conditional logic issues

Security & Quality:
- All input validation rules updated with latest security patterns
- Cross-platform compatibility improvements maintained
- Comprehensive error handling and retry logic preserved
- Modern development tooling and best practices adopted

This commit addresses 100% of actionable feedback from PR review analysis,
implements comprehensive testing infrastructure, and maintains high code
quality standards across all 41 GitHub Actions.

* feat: enhance expression handling and version parsing

- Fix node-setup force-version expression logic for proper empty string handling
- Improve version-file-parser with secure regex validation and enhanced Python detection
- Add CodeRabbit configuration for CalVer versioning and README review guidance

* feat(validate-inputs): implement modular validation system

- Add modular validator architecture with specialized validators
- Implement base validator classes for different input types
- Add validators: boolean, docker, file, network, numeric, security, token, version
- Add convention mapper for automatic input validation
- Add comprehensive documentation for the validation system
- Implement PCRE regex support and injection protection

* feat(validate-inputs): add validation rules for all actions

- Add YAML validation rules for 42 GitHub Actions
- Auto-generated rules with convention mappings
- Include metadata for validation coverage and quality indicators
- Mark rules as auto-generated to prevent manual edits

* test(validate-inputs): add comprehensive test suite for validators

- Add unit tests for all validator modules
- Add integration tests for the validation system
- Add fixtures for version test data
- Test coverage for boolean, docker, file, network, numeric, security, token, and version validators
- Add tests for convention mapper and registry

* feat(tools): add validation scripts and utilities

- Add update-validators.py script for auto-generating rules
- Add benchmark-validator.py for performance testing
- Add debug-validator.py for troubleshooting
- Add generate-tests.py for test generation
- Add check-rules-not-manually-edited.sh for CI validation
- Add fix-local-action-refs.py tool for fixing action references

* feat(actions): add CustomValidator.py files for specialized validation

- Add custom validators for actions requiring special validation logic
- Implement validators for docker, go, node, npm, php, python, terraform actions
- Add specialized validation for compress-images, common-cache, common-file-check
- Implement version detection validators with language-specific logic
- Add validation for build arguments, architectures, and version formats

* test: update ShellSpec test framework for Python validation

- Update all validation.spec.sh files to use Python validator
- Add shared validation_core.py for common test utilities
- Remove obsolete bash validation helpers
- Update test output expectations for Python validator format
- Add codeql-analysis test suite
- Refactor framework utilities for Python integration
- Remove deprecated test files

* feat(actions): update action.yml files to use validate-inputs

- Replace inline bash validation with validate-inputs action
- Standardize validation across all 42 actions
- Add new codeql-analysis action
- Update action metadata and branding
- Add validation step as first step in composite actions
- Maintain backward compatibility with existing inputs/outputs

* ci: update GitHub workflows for enhanced security and testing

- Add new codeql-new.yml workflow
- Update security scanning workflows
- Enhance dependency review configuration
- Update test-actions workflow for new validation system
- Improve workflow permissions and security settings
- Update action versions to latest SHA-pinned releases

* build: update build configuration and dependencies

- Update Makefile with new validation targets
- Add Python dependencies in pyproject.toml
- Update npm dependencies and scripts
- Enhance Docker testing tools configuration
- Add targets for validator updates and local ref fixes
- Configure uv for Python package management

* chore: update linting and documentation configuration

- Update EditorConfig settings for consistent formatting
- Enhance pre-commit hooks configuration
- Update prettier and yamllint ignore patterns
- Update gitleaks security scanning rules
- Update CodeRabbit review configuration
- Update CLAUDE.md with latest project standards and rules

* docs: update Serena memory files and project metadata

- Remove obsolete PR-186 memory files
- Update project overview with current architecture
- Update project structure documentation
- Add quality standards and communication guidelines
- Add modular validator architecture documentation
- Add shellspec testing framework documentation
- Update project.yml with latest configuration

* feat: moved rules.yml to same folder as action, fixes

* fix(validators): correct token patterns and fix validator bugs

- Fix GitHub classic PAT pattern: ghp_ + 36 chars = 40 total
- Fix GitHub fine-grained PAT pattern: github_pat_ + 71 chars = 82 total
- Initialize result variable in convention_mapper to prevent UnboundLocalError
- Fix empty URL validation in network validator to return error
- Add GitHub expression check to docker architectures validator
- Update docker-build CustomValidator parallel-builds max to 16

* test(validators): fix test fixtures and expectations

- Fix token lengths in test data: github_pat 71 chars, ghp/gho 36 chars
- Update integration tests with correct token lengths
- Fix file validator test to expect absolute paths rejected for security
- Rename TestGenerator import to avoid pytest collection warning
- Update custom validator tests with correct input names
- Change docker-build tests: platforms->architectures, tags->tag
- Update docker-publish tests to match new registry enum validation

* test(shellspec): fix token lengths in test helpers and specs

- Fix default token lengths in spec_helper.sh to use correct 40-char format
- Update csharp-publish default tokens in 4 locations
- Update codeql-analysis default tokens in 2 locations
- Fix codeql-analysis test tokens to correct lengths (40 and 82 chars)
- Fix npm-publish fine-grained token test to use 82-char format

* feat(actions): add permissions documentation and environment variable usage

- Add permissions comments to all action.yml files documenting required GitHub permissions
- Convert direct input usage to environment variables in shell steps for security
- Add validation steps with proper error handling
- Update input descriptions and add security notes where applicable
- Ensure all actions follow consistent patterns for input validation

* chore(workflows): update GitHub Actions workflow versions

- Update workflow action versions to latest
- Improve workflow consistency and maintainability

* docs(security): add comprehensive security policy

- Document security features and best practices
- Add vulnerability reporting process
- Include audit history and security testing information

* docs(memory): add GitHub workflow reference documentation

- Add GitHub Actions workflow commands reference
- Add GitHub workflow expressions guide
- Add secure workflow usage patterns and best practices

* chore: token optimization, code style conventions
* chore: cr fixes
* fix: trivy reported Dockerfile problems
* fix(security): more security fixes
* chore: dockerfile and make targets for publishing
* fix(ci): add creds to test-actions workflow
* fix: security fix and checkout step to codeql-new
* chore: test fixes
* fix(security): codeql detected issues
* chore: code review fixes, ReDos protection
* style: apply MegaLinter fixes
* fix(ci): missing packages read permission
* fix(ci): add missing working directory setting
* chore: linting, add validation-regex to use regex_pattern
* chore: code review fixes
* chore(deps): update actions
* fix(security): codeql fixes
* chore(cr): apply cr comments
* chore: improve POSIX compatibility
* chore(cr): apply cr comments
* fix: codeql warning in Dockerfile, build failures
* chore(cr): apply cr comments
* fix: docker-testing-tools/Dockerfile
* chore(cr): apply cr comments
* fix(docker): update testing-tools image for GitHub Actions compatibility
* chore(cr): apply cr comments
* feat: add more tests, fix issues
* chore: fix codeql issues, update actions
* chore(cr): apply cr comments
* fix: integration tests
* chore: deduplication and fixes
* style: apply MegaLinter fixes
* chore(cr): apply cr comments
* feat: dry-run mode for generate-tests
* fix(ci): kcov installation
* chore(cr): apply cr comments
* chore(cr): apply cr comments
* chore(cr): apply cr comments
* chore(cr): apply cr comments, simplify action testing, use uv
* fix: run-tests.sh action counting
* chore(cr): apply cr comments
* chore(cr): apply cr comments
This commit is contained in:
2025-10-14 13:37:58 +03:00
committed by GitHub
parent d3cc8d4790
commit 78fdad69e5
353 changed files with 55370 additions and 1714 deletions

View File

@@ -0,0 +1,280 @@
# GitHub Actions Testing Framework Docker Image
# Multi-stage build with non-root user for security
# Pre-installs all testing tools to reduce CI runtime
# Centralized ARG defaults to avoid version drift across stages
ARG KCOV_VERSION=42
ARG TRUFFLEHOG_VERSION=3.86.0
ARG ACTIONLINT_VERSION=1.7.7
ARG ACT_VERSION=0.2.71
ARG SHELLSPEC_VERSION=0.28.1
# Stage 1: Build kcov separately to keep final image slim
FROM ubuntu:22.04 AS kcov-builder
ARG KCOV_VERSION
# Install only build dependencies needed for kcov
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
ca-certificates \
cmake \
g++ \
git \
libcurl4-openssl-dev \
libdw-dev \
libelf-dev \
libiberty-dev \
libssl-dev \
make \
pkg-config \
python3 \
zlib1g-dev \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Build kcov from source
WORKDIR /tmp/kcov-build
RUN git clone --depth 1 --branch "v${KCOV_VERSION}" https://github.com/SimonKagstrom/kcov.git .
WORKDIR /tmp/kcov-build/build
RUN cmake .. \
&& make \
&& make install DESTDIR=/kcov-install
# Stage 2: Base system setup
FROM ubuntu:22.04 AS base
LABEL maintainer="ivuorinen"
LABEL description="GitHub Actions testing framework with pre-installed tools"
LABEL version="1.0.0"
LABEL org.opencontainers.image.source="https://github.com/ivuorinen/actions"
# Avoid interactive prompts during package installation
ENV DEBIAN_FRONTEND=noninteractive
ENV TZ=UTC
ENV NODE_MAJOR=20
# Set shell to bash with pipefail for better error handling
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# Install system dependencies and common tools
# hadolint ignore=DL3008
RUN apt-get update && apt-get install -y \
--no-install-recommends \
ca-certificates \
curl \
git \
gnupg \
gzip \
jq \
lsb-release \
python3 \
python3-pip \
python3-yaml \
shellcheck \
sudo \
tar \
unzip \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* \
# Note: build-essential, cmake, and kcov build deps moved to separate builder stage \
&& curl -fsSL --proto '=https' --tlsv1.2 https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key -o /tmp/nodesource.gpg.key \
&& gpg --dearmor -o /usr/share/keyrings/nodesource.gpg < /tmp/nodesource.gpg.key \
&& echo "deb [signed-by=/usr/share/keyrings/nodesource.gpg] https://deb.nodesource.com/node_${NODE_MAJOR}.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list \
&& apt-get update \
&& apt-get install -y --no-install-recommends nodejs \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/nodesource.gpg.key
# Stage 2: Tool installation
FROM base AS tools
# Set shell to bash with pipefail for better error handling
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# Version pinning for security and reproducibility (inherit from global ARGs)
ARG TRUFFLEHOG_VERSION
ARG ACTIONLINT_VERSION
ARG ACT_VERSION
ARG SHELLSPEC_VERSION
# Install all APT-based and standalone tools in a single optimized layer
# 1. Configure APT repositories (Trivy, GitHub CLI)
# 2. Install APT packages (trivy, gh, xz-utils)
# 3. Download all tool tarballs and checksums in parallel
# 4. Verify checksums and install tools
# hadolint ignore=DL3008
RUN set -eux \
# Detect architecture once
&& arch="$(dpkg --print-architecture)" \
&& case "${arch}" in \
amd64) trufflehog_arch="amd64"; actionlint_arch="amd64"; act_arch="Linux_x86_64" ;; \
arm64) trufflehog_arch="arm64"; actionlint_arch="arm64"; act_arch="Linux_arm64" ;; \
*) echo "Unsupported architecture: ${arch}" && exit 1 ;; \
esac \
# Configure APT repositories for Trivy and GitHub CLI
&& echo "=== Configuring APT repositories ===" \
&& curl -fsSL --proto '=https' --tlsv1.2 https://aquasecurity.github.io/trivy-repo/deb/public.key -o /tmp/trivy.key \
&& gpg --dearmor -o /usr/share/keyrings/trivy.gpg < /tmp/trivy.key \
&& echo "deb [signed-by=/usr/share/keyrings/trivy.gpg] https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main" \
| tee /etc/apt/sources.list.d/trivy.list \
&& curl -fsSL --proto '=https' --tlsv1.2 https://cli.github.com/packages/githubcli-archive-keyring.gpg -o /tmp/githubcli-archive-keyring.gpg \
&& install -m 0644 /tmp/githubcli-archive-keyring.gpg /usr/share/keyrings/githubcli-archive-keyring.gpg \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \
| tee /etc/apt/sources.list.d/github-cli.list > /dev/null \
# Install APT packages
&& echo "=== Installing APT packages ===" \
&& apt-get update \
&& apt-get install -y --no-install-recommends gh trivy xz-utils \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/trivy.key /tmp/githubcli-archive-keyring.gpg \
# Download all tool tarballs and checksums
&& echo "=== Downloading standalone tools ===" \
&& trufflehog_tarball="trufflehog_${TRUFFLEHOG_VERSION}_linux_${trufflehog_arch}.tar.gz" \
&& actionlint_tarball="actionlint_${ACTIONLINT_VERSION}_linux_${actionlint_arch}.tar.gz" \
&& act_tarball="act_${act_arch}.tar.gz" \
&& curl -fsSL --proto '=https' --tlsv1.2 "https://github.com/trufflesecurity/trufflehog/releases/download/v${TRUFFLEHOG_VERSION}/${trufflehog_tarball}" -o "/tmp/${trufflehog_tarball}" \
&& curl -fsSL --proto '=https' --tlsv1.2 "https://github.com/trufflesecurity/trufflehog/releases/download/v${TRUFFLEHOG_VERSION}/trufflehog_${TRUFFLEHOG_VERSION}_checksums.txt" -o /tmp/trufflehog_checksums.txt \
&& curl -fsSL --proto '=https' --tlsv1.2 "https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/${actionlint_tarball}" -o "/tmp/${actionlint_tarball}" \
&& curl -fsSL --proto '=https' --tlsv1.2 "https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_checksums.txt" -o /tmp/actionlint_checksums.txt \
&& curl -fsSL --proto '=https' --tlsv1.2 "https://github.com/nektos/act/releases/download/v${ACT_VERSION}/${act_tarball}" -o "/tmp/${act_tarball}" \
&& curl -fsSL --proto '=https' --tlsv1.2 "https://github.com/nektos/act/releases/download/v${ACT_VERSION}/checksums.txt" -o /tmp/act_checksums.txt \
# Verify checksums and install trufflehog
&& echo "=== Verifying checksums and installing tools ===" \
&& grep "${trufflehog_tarball}" /tmp/trufflehog_checksums.txt \
| sed "s|${trufflehog_tarball}|/tmp/${trufflehog_tarball}|" \
| sha256sum -c - \
&& tar -xzf "/tmp/${trufflehog_tarball}" -C /tmp \
&& chmod +x /tmp/trufflehog \
&& mv /tmp/trufflehog /usr/local/bin/trufflehog \
# Verify checksum and install actionlint
&& grep "${actionlint_tarball}" /tmp/actionlint_checksums.txt \
| sed "s|${actionlint_tarball}|/tmp/${actionlint_tarball}|" \
| sha256sum -c - \
&& tar -xzf "/tmp/${actionlint_tarball}" -C /tmp \
&& chmod +x /tmp/actionlint \
&& mv /tmp/actionlint /usr/local/bin/actionlint \
# Verify checksum and install act
&& grep "${act_tarball}" /tmp/act_checksums.txt \
| sed "s|${act_tarball}|/tmp/${act_tarball}|" \
| sha256sum -c - \
&& tar -xzf "/tmp/${act_tarball}" -C /tmp \
&& chmod +x /tmp/act \
&& mv /tmp/act /usr/local/bin/act \
# Clean up all temporary files
&& rm -f /tmp/*.tar.gz /tmp/*_checksums.txt \
# Verify all installations
&& echo "=== Verifying tool installations ===" \
&& trivy --version \
&& gh --version \
&& trufflehog --version \
&& actionlint --version \
&& act --version \
&& test -f /bin/sh && test -f /bin/bash && echo "✓ Shell binaries intact" \
&& echo "=== All tools installed successfully ==="
# Stage 3: Final image with non-root user
FROM tools AS final
# Set shell to bash with pipefail for better error handling
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# Create non-root user for security
ARG USERNAME=runner
ARG USER_UID=1001
ARG USER_GID=$USER_UID
ARG SHELLSPEC_VERSION
# Set up environment for testing
ENV PATH="/home/$USERNAME/.local/bin:$PATH"
ENV USER=$USERNAME
ENV HOME="/home/$USERNAME"
# Create the user and group, then
# grant passwordless sudo to runner user for testing scenarios, then
# create workspace directory with proper permissions (as root)
RUN groupadd --gid "$USER_GID" "$USERNAME" \
&& useradd --uid "$USER_UID" --gid "$USER_GID" -m "$USERNAME" -s /bin/bash \
&& echo "$USERNAME ALL=(ALL) NOPASSWD:ALL" > "/etc/sudoers.d/$USERNAME" \
&& chmod 0440 "/etc/sudoers.d/$USERNAME" \
&& mkdir -p /workspace \
&& chown -R "$USERNAME:$USERNAME" /workspace
# Copy kcov from builder stage (avoiding build dependencies in final image)
# kcov is not available in Ubuntu 22.04 apt repositories, so we build it separately
COPY --from=kcov-builder /kcov-install/usr/local/ /usr/local/
# Install only runtime dependencies for kcov (not build dependencies)
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
libcurl4 \
libdw1 \
libelf1 \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Switch to non-root user for ShellSpec installation
USER "$USERNAME"
WORKDIR /workspace
# Install ShellSpec testing framework in user's home with checksum verification, then
# verify installations (run as root to access all tools)
# ShellSpec - version-aware checksum verification
# hadolint ignore=SC2016
RUN set -eux; \
mkdir -p ~/.local/bin; \
tarball="shellspec-dist.tar.gz"; \
# Pinned SHA-256 checksum for ShellSpec 0.28.1 shellspec-dist.tar.gz
# Source: https://github.com/shellspec/shellspec/releases/download/0.28.1/shellspec-dist.tar.gz
expected_checksum="350d3de04ba61505c54eda31a3c2ee912700f1758b1a80a284bc08fd8b6c5992"; \
\
# Download ShellSpec
curl -fsSL --proto '=https' --tlsv1.2 \
"https://github.com/shellspec/shellspec/releases/download/${SHELLSPEC_VERSION}/${tarball}" \
-o "/tmp/${tarball}"; \
\
# Verify checksum
actual_checksum=$(sha256sum "/tmp/${tarball}" | awk '{print $1}'); \
if [ "${actual_checksum}" != "${expected_checksum}" ]; then \
echo "Error: Checksum verification failed for ShellSpec ${SHELLSPEC_VERSION}" >&2; \
echo "Expected: ${expected_checksum}" >&2; \
echo "Got: ${actual_checksum}" >&2; \
rm -f "/tmp/${tarball}"; \
exit 1; \
fi; \
echo "Checksum verified successfully"; \
\
tar -xzf "/tmp/${tarball}" -C "$HOME/.local"; \
ln -s "$HOME/.local/shellspec/shellspec" "$HOME/.local/bin/shellspec"; \
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc; \
shellspec --version; \
rm -f "/tmp/${tarball}" \
&& echo "ShellSpec installed successfully" \
&& echo "Verifying installed tool versions..." \
&& echo "=== Tool Versions ===" \
&& shellcheck --version \
&& jq --version \
&& kcov --version \
&& trivy --version \
&& trufflehog --version \
&& actionlint --version \
&& act --version \
&& gh --version \
&& node --version \
&& npm --version \
&& python3 --version \
&& echo "=== System tools verified ===" \
&& echo "=== Verify user-installed tools ===" \
&& shellspec --version \
&& echo "=== User tools verified ===" \
&& echo "=== Build complete ==="
# Health check to verify essential tools are accessible
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD shellspec --version > /dev/null 2>&1 && \
shellcheck --version > /dev/null 2>&1 && \
jq --version > /dev/null 2>&1 || exit 1
# Default command keeps container running for GitHub Actions
CMD ["/bin/bash", "-c", "tail -f /dev/null"]

View File

@@ -0,0 +1,172 @@
# GitHub Actions Testing Docker Image
Pre-built Docker image with all testing tools to eliminate CI setup time and ensure consistent environments.
## 🚀 Quick Start
```yaml
jobs:
test:
runs-on: ubuntu-latest
container: ghcr.io/ivuorinen/actions:testing-tools
steps:
- uses: actions/checkout@v5
- run: shellspec _tests/unit/your-action/
```
## 📦 Pre-installed Tools
| Tool | Version | Purpose |
|----------------|-----------------|---------------------------------|
| **ShellSpec** | 0.28.1 (pinned) | Shell script testing framework |
| **nektos/act** | 0.2.71 (pinned) | Local GitHub Actions testing |
| **TruffleHog** | 3.86.0 (pinned) | Secrets detection |
| **actionlint** | 1.7.7 (pinned) | GitHub Actions linting |
| **Trivy** | repo stable¹ | Container security scanning |
| **GitHub CLI** | repo stable¹ | GitHub API interactions |
| **shellcheck** | repo stable¹ | Shell script linting |
| **jq** | repo stable¹ | JSON processing |
| **kcov** | v42 (source)² | Code coverage for shell scripts |
| **Node.js** | LTS | JavaScript runtime |
| **Python** | 3.x | Python runtime + PyYAML |
¹ _Installed via Ubuntu 22.04 LTS repositories for stability and security_
² _Built from source (not available in Ubuntu 22.04 repositories)_
## 🏗️ Building Locally
```bash
cd _tools/docker-testing-tools
./build.sh [tag] # Build and basic test
./test.sh [tag] # Comprehensive testing
```
## 📊 Performance Benefits
| Workflow Job | Before | After | Savings |
|-------------------|--------|-------|----------------|
| Unit Tests | ~90s | ~30s | **60s** |
| Integration Tests | ~120s | ~45s | **75s** |
| Coverage | ~100s | ~40s | **60s** |
| **Total per run** | ~310s | ~115s | **~3 minutes** |
## 🏗️ Multi-Stage Build Benefits
The Dockerfile uses a **3-stage build process**:
1. **`base`** - System dependencies and Node.js installation
2. **`tools`** - Tool installation (Trivy, GitHub CLI, standalone tools)
3. **`final`** - User setup, ShellSpec installation, and verification
**Advantages:**
-**Faster builds** - Docker layer caching optimizes repeated builds
- 📦 **Smaller images** - Only final stage included in image
- 🔒 **Better security** - Build-time dependencies not included in final image
- 🧹 **Cleaner separation** - System vs user tool installation isolated
## 🔧 Usage Examples
### Basic Testing
```yaml
jobs:
test:
runs-on: ubuntu-latest
container: ghcr.io/ivuorinen/actions:testing-tools
steps:
- uses: actions/checkout@v5
- run: npm ci
- run: shellspec _tests/unit/
```
### With Coverage
```yaml
jobs:
coverage:
runs-on: ubuntu-latest
container: ghcr.io/ivuorinen/actions:testing-tools
steps:
- uses: actions/checkout@v5
- run: make test-coverage
- run: kcov --include-pattern=_tests/ coverage/ _tests/run-tests.sh
```
### Integration Testing
```yaml
jobs:
integration:
runs-on: ubuntu-latest
container: ghcr.io/ivuorinen/actions:testing-tools
steps:
- uses: actions/checkout@v5
- run: act workflow_dispatch -W _tests/integration/workflows/
```
## 🐋 Image Variants
- `testing-tools` - Latest stable build from main branch
- `main-testing-tools` - Latest build from main branch
- `pr-*-testing-tools` - Pull request builds for testing
## 🔒 Security
The image is:
-**Multi-stage build** - Reduced final image size and attack surface
-**Non-root user** - Runs as `runner` user (uid: 1001) by default
-**Built from official Ubuntu 22.04 LTS** - Secure and maintained base
-**Scanned with Trivy** for vulnerabilities during build
-**Specific tool versions** - No `latest` tags where avoidable
-**Minimal attack surface** - Only testing tools included
-**Sudo access** - Available for emergency use only
-**Transparent build** - Built with GitHub Actions
## 🚨 Migration Guide
### Before (Old Workflow)
```yaml
- name: Install ShellSpec
run: curl -fsSL https://git.io/shellspec | sh -s -- --yes
- name: Install tools
run: |
sudo apt-get update
sudo apt-get install -y jq shellcheck
# Note: kcov must be built from source on Ubuntu 22.04+
```
### After (With Container)
```yaml
jobs:
test:
container: ghcr.io/ivuorinen/actions:testing-tools
# All tools pre-installed! 🎉
```
## 🤝 Contributing
1. Update `Dockerfile` with new tools
2. Test locally with `./build.sh`
3. Submit PR - image builds automatically
4. After merge, image is available as `:testing-tools`
## 📝 Changelog
### v1.1.0
- 🔒 **Security improvements**: Multi-stage build with non-root user
- 🏗️ **Multi-stage Dockerfile**: Optimized build process and smaller final image
- 👤 **Non-root user**: Runs as `runner` user (uid: 1001) for security
- 🧪 **Comprehensive testing**: Added `test.sh` for thorough validation
- 📦 **Better organization**: Improved build stages and tool installation
### v1.0.0
- Initial release with all testing tools
- ShellSpec, act, Trivy, TruffleHog, actionlint
- Node.js LTS, Python 3, essential utilities
- Multi-architecture support (amd64, arm64)

View File

@@ -0,0 +1,56 @@
#!/usr/bin/env bash
# Build script for GitHub Actions Testing Docker Image
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
IMAGE_NAME="ghcr.io/ivuorinen/actions"
IMAGE_TAG="${1:-testing-tools}"
FULL_IMAGE_NAME="${IMAGE_NAME}:${IMAGE_TAG}"
echo "Building GitHub Actions Testing Docker Image..."
echo "Image: $FULL_IMAGE_NAME"
# Enable BuildKit for better caching and performance
export DOCKER_BUILDKIT=1
# Build the multi-stage image
# Check for buildx support up front, then run the appropriate build command
if docker buildx version >/dev/null 2>&1; then
echo "Using buildx (multi-arch capable)"
docker buildx build \
--pull \
--tag "$FULL_IMAGE_NAME" \
--file "$SCRIPT_DIR/Dockerfile" \
--target final \
--load \
"$SCRIPT_DIR"
else
echo "⚠️ buildx not available, using standard docker build"
docker build \
--pull \
--tag "$FULL_IMAGE_NAME" \
--file "$SCRIPT_DIR/Dockerfile" \
--target final \
"$SCRIPT_DIR"
fi
echo "Build completed successfully!"
echo ""
echo "Testing the image..."
# Test basic functionality
docker run --rm "$FULL_IMAGE_NAME" whoami
docker run --rm "$FULL_IMAGE_NAME" shellspec --version
docker run --rm "$FULL_IMAGE_NAME" act --version
echo "Image tests passed!"
echo ""
echo "To test the image locally:"
echo " docker run --rm -it $FULL_IMAGE_NAME"
echo ""
echo "To push to registry:"
echo " docker push $FULL_IMAGE_NAME"
echo ""
echo "To use in GitHub Actions:"
echo " container: $FULL_IMAGE_NAME"

View File

@@ -0,0 +1,6 @@
--require spec_helper
# ShellSpec configuration for Docker testing environment
--format documentation
--color
--reportdir reports

View File

@@ -0,0 +1,37 @@
#!/usr/bin/env bash
# Basic ShellSpec test to verify the testing framework works correctly
Describe 'Docker Testing Environment'
It 'has correct user'
When call whoami
The status should be success
The output should equal "${EXPECTED_USER:-runner}"
End
It 'can access workspace'
When call pwd
The status should be success
The output should include "${EXPECTED_WORKSPACE:-/workspace}"
End
It 'has ShellSpec available'
When call shellspec --version
The status should be success
The output should include "shellspec"
End
It 'has required tools'
When call which jq
The status should be success
End
It 'can write to workspace'
When call touch test-write-file
The status should be success
End
It 'can clean up test files'
When call rm -f test-write-file
The status should be success
End
End

View File

@@ -0,0 +1,14 @@
#!/bin/sh
# ShellSpec helper for Docker testing environment
# Set up common test environment
set -eu
# Helper functions for tests
ensure_workspace() {
[ -d /workspace ] || mkdir -p /workspace
}
cleanup_test_files() {
find /workspace -name "test-*" -type f -delete 2>/dev/null || true
}

View File

@@ -0,0 +1,162 @@
#!/bin/sh
# Test script for GitHub Actions Testing Docker Image
# Verifies all tools work correctly with non-root user
set -eu
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
# Accept full image reference or component parts
# Priority: IMAGE_REF env > FULL_IMAGE env > digest in first arg > construct from parts
if [ -n "${IMAGE_REF:-}" ]; then
# Explicit full image reference (supports both tag and digest)
FULL_IMAGE_NAME="$IMAGE_REF"
elif [ -n "${FULL_IMAGE:-}" ]; then
# Alternative env var for full image reference
FULL_IMAGE_NAME="$FULL_IMAGE"
elif [ $# -gt 0 ] && echo "$1" | grep -q '@'; then
# First arg is a digest-based reference (e.g., ghcr.io/owner/repo@sha256:...)
FULL_IMAGE_NAME="$1"
else
# Construct from component parts with defaults
IMAGE_OWNER="${IMAGE_OWNER:-ivuorinen}"
IMAGE_REPO="${IMAGE_REPO:-actions}"
# For backwards compatibility, use first arg as tag if no IMAGE_TAG env var set
IMAGE_TAG="${IMAGE_TAG:-${1:-testing-tools}}"
FULL_IMAGE_NAME="ghcr.io/${IMAGE_OWNER}/${IMAGE_REPO}:${IMAGE_TAG}"
fi
echo "Testing GitHub Actions Testing Docker Image: $FULL_IMAGE_NAME"
echo "=============================================================="
# Test 1: User information
echo "1. Testing user setup..."
USER_INFO=$(docker run --rm "$FULL_IMAGE_NAME" bash -c "whoami && id")
echo "User info: $USER_INFO"
if echo "$USER_INFO" | grep -q "runner"; then
echo "✅ Non-root user 'runner' is correctly set"
else
echo "❌ Expected non-root user 'runner', got: $USER_INFO"
exit 1
fi
# Test 2: ShellSpec (user-installed)
echo ""
echo "2. Testing ShellSpec..."
SHELLSPEC_VERSION=$(docker run --rm "$FULL_IMAGE_NAME" shellspec --version)
echo "ShellSpec: $SHELLSPEC_VERSION"
if echo "$SHELLSPEC_VERSION" | grep -q "0\."; then
echo "✅ ShellSpec is working"
else
echo "❌ ShellSpec test failed"
exit 1
fi
# Test 3: System tools (root-installed)
echo ""
echo "3. Testing system tools..."
# Test each tool individually (POSIX compatible)
for tool_cmd in \
"act --version" \
"trivy --version" \
"trufflehog --version" \
"actionlint --version" \
"shellcheck --version" \
"jq --version" \
"kcov --version" \
"gh --version" \
"node --version" \
"npm --version" \
"python3 --version"
do
printf " Testing %s... " "$tool_cmd"
if docker run --rm "$FULL_IMAGE_NAME" sh -c "$tool_cmd" >/dev/null 2>&1; then
echo "✅"
else
echo "❌"
exit 1
fi
done
# Test 4: File permissions
echo ""
echo "4. Testing file permissions..."
WORKSPACE_PERMS=$(docker run --rm "$FULL_IMAGE_NAME" bash -c "ls -ld /workspace")
echo "Workspace permissions: $WORKSPACE_PERMS"
if echo "$WORKSPACE_PERMS" | grep -q "runner runner"; then
echo "✅ Workspace has correct ownership"
else
echo "❌ Workspace permissions issue"
exit 1
fi
# Test 5: Write permissions
echo ""
echo "5. Testing write permissions..."
if docker run --rm "$FULL_IMAGE_NAME" bash -c "touch /workspace/test-file && rm /workspace/test-file"; then
echo "✅ User can write to workspace"
else
echo "❌ User cannot write to workspace"
exit 1
fi
# Test 6: Sudo access (should work but not needed for normal operations)
echo ""
echo "6. Testing sudo access..."
if docker run --rm "$FULL_IMAGE_NAME" sudo whoami | grep -q "root"; then
echo "✅ Sudo access works (for emergency use)"
else
echo "❌ Sudo access not working"
exit 1
fi
# Test 7: Environment variables
echo ""
echo "7. Testing environment variables..."
ENV_CHECK=$(docker run --rm "$FULL_IMAGE_NAME" sh -c "echo \$USER:\$HOME:\$PATH")
echo "Environment: $ENV_CHECK"
if echo "$ENV_CHECK" | grep -q "runner" && echo "$ENV_CHECK" | grep -q "/home/runner" && echo "$ENV_CHECK" | grep -q ".local/bin"; then
echo "✅ Environment variables are correct"
else
echo "❌ Environment variables issue"
exit 1
fi
# Test 8: Real ShellSpec test with local test files
echo ""
echo "8. Testing ShellSpec with local test files..."
if [ -d "$SCRIPT_DIR/test-files" ]; then
# Mount local test directory and run a real ShellSpec test
if docker run --rm -v "$SCRIPT_DIR/test-files:/workspace/test-files" "$FULL_IMAGE_NAME" \
sh -c "cd /workspace/test-files && shellspec --format tap basic_spec.sh" >/dev/null 2>&1; then
echo "✅ ShellSpec can run real tests with mounted files"
else
echo "❌ ShellSpec test with local files failed"
exit 1
fi
else
echo "⚠️ No test-files directory found, creating sample test..."
# Create a temporary test to verify mounting and execution works
if docker run --rm -v "$SCRIPT_DIR:/workspace/scripts" "$FULL_IMAGE_NAME" \
sh -c "echo 'basic test works' && ls -la /workspace/scripts" >/dev/null 2>&1; then
echo "✅ Volume mounting and script directory access works"
else
echo "❌ Volume mounting test failed"
exit 1
fi
fi
echo ""
echo "🎉 All tests passed! The Docker image is working correctly with:"
echo " - Non-root user 'runner' (uid: 1001)"
echo " - All testing tools installed and accessible"
echo " - Proper file permissions and workspace access"
echo " - Secure sudo configuration for emergency use"
echo ""
echo "Image size:"
docker images "$FULL_IMAGE_NAME" --format "table {{.Repository}}:{{.Tag}}\t{{.Size}}"

277
_tools/fix-local-action-refs.py Executable file
View File

@@ -0,0 +1,277 @@
#!/usr/bin/env python3
"""Fix local action references in GitHub Action YAML files.
This script finds and fixes uses: ../action-name references to use: ./action-name
following GitHub's recommended pattern for same-repository action references.
Usage:
python3 fix-local-action-refs.py [--check] [--dry-run]
Options:
--check Check for issues without fixing (exit code 1 if issues found)
--dry-run Show what would be changed without making changes
--help Show this help message
Examples:
python3 fix-local-action-refs.py --check # Check for issues
python3 fix-local-action-refs.py --dry-run # Preview changes
python3 fix-local-action-refs.py # Fix all issues
"""
from __future__ import annotations
import argparse
from pathlib import Path
import re
import sys
class LocalActionRefsFixer:
"""Fix local action references from ../action-name to ./action-name pattern."""
def __init__(self, project_root: Path | None = None) -> None:
"""Initialize with project root directory."""
if project_root is None:
# Assume script is in _tools/ directory
script_dir = Path(__file__).resolve().parent
self.project_root = script_dir.parent
else:
self.project_root = Path(project_root).resolve()
def find_action_files(self) -> list[Path]:
"""Find all action.yml files in the project."""
action_files = []
# Look for action.yml files in top-level directories
for item in self.project_root.iterdir():
if item.is_dir() and not item.name.startswith(".") and not item.name.startswith("_"):
action_file = item / "action.yml"
if action_file.exists():
action_files.append(action_file)
return sorted(action_files)
def get_available_actions(self) -> list[str]:
"""Get list of available action names in the repository."""
actions = []
for action_file in self.find_action_files():
action_name = action_file.parent.name
actions.append(action_name)
return sorted(actions)
def find_local_ref_issues(self, content: str) -> list[tuple[int, str, str, str]]:
"""Find lines with ../action-name references that should be ./action-name.
Returns:
List of (line_number, line_content, old_ref, new_ref) tuples
"""
issues = []
available_actions = self.get_available_actions()
# Pattern to match "uses: ../action-name" references
pattern = re.compile(r"^(\s*uses:\s+)\.\./([\w-]+)(\s*(?:#.*)?)\s*$")
lines = content.splitlines()
for line_num, line in enumerate(lines, 1):
match = pattern.match(line)
if match:
_prefix, action_name, _suffix = match.groups()
# Only fix if this is actually one of our actions
if action_name in available_actions:
old_ref = f"../{action_name}"
new_ref = f"./{action_name}"
issues.append((line_num, line, old_ref, new_ref))
return issues
def fix_content(self, content: str) -> tuple[str, int]:
"""Fix ../action-name references to ./action-name in content.
Returns:
Tuple of (fixed_content, number_of_fixes)
"""
available_actions = self.get_available_actions()
fixes_made = 0
# Pattern to match and replace "uses: ../action-name" references
def replace_ref(match: re.Match[str]) -> str:
nonlocal fixes_made
prefix, action_name, suffix = match.groups()
# Only fix if this is actually one of our actions
if action_name in available_actions:
fixes_made += 1
return f"{prefix}./{action_name}{suffix}"
# Don't change external references
return match.group(0)
pattern = re.compile(r"^(\s*uses:\s+)\.\./([\w-]+)(\s*(?:#.*)?)\s*$", re.MULTILINE)
fixed_content = pattern.sub(replace_ref, content)
return fixed_content, fixes_made
def check_file(self, file_path: Path) -> dict:
"""Check a single file for local action reference issues.
Returns:
Dict with file info and issues found
"""
try:
content = file_path.read_text(encoding="utf-8")
issues = self.find_local_ref_issues(content)
return {"file": file_path, "issues": issues, "error": None}
except Exception as e:
return {"file": file_path, "issues": [], "error": str(e)}
def fix_file(self, file_path: Path, *, dry_run: bool = False) -> dict:
"""Fix local action references in a single file.
Returns:
Dict with file info and fixes made
"""
try:
content = file_path.read_text(encoding="utf-8")
fixed_content, fixes_made = self.fix_content(content)
if fixes_made > 0 and not dry_run:
file_path.write_text(fixed_content, encoding="utf-8")
return {"file": file_path, "fixes_made": fixes_made, "error": None}
except Exception as e:
return {"file": file_path, "fixes_made": 0, "error": str(e)}
def check_all_files(self) -> list[dict]:
"""Check all action files for issues."""
results = []
action_files = self.find_action_files()
for file_path in action_files:
result = self.check_file(file_path)
if result["issues"] or result["error"]:
results.append(result)
return results
def fix_all_files(self, *, dry_run: bool = False) -> list[dict]:
"""Fix all action files."""
results = []
action_files = self.find_action_files()
for file_path in action_files:
result = self.fix_file(file_path, dry_run=dry_run)
if result["fixes_made"] > 0 or result["error"]:
results.append(result)
return results
def _create_argument_parser() -> argparse.ArgumentParser:
"""Create and configure the argument parser."""
docstring = "" if __doc__ is None else __doc__
parser = argparse.ArgumentParser(
description="Fix local action references from ../action-name to ./action-name",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=docstring.split("Usage:")[1] if "Usage:" in docstring else None,
)
parser.add_argument(
"--check",
action="store_true",
help="Check for issues without fixing (exit 1 if issues found)",
)
parser.add_argument(
"--dry-run",
action="store_true",
help="Show what would be changed without making changes",
)
return parser
def _run_check_mode(fixer: LocalActionRefsFixer) -> int:
"""Run in check mode and return exit code."""
print("🔍 Checking for local action reference issues...")
results = fixer.check_all_files()
if not results:
print("✅ No local action reference issues found!")
return 0
total_issues = 0
for result in results:
if result["error"]:
print(f"❌ Error checking {result['file']}: {result['error']}")
continue
file_path = result["file"]
issues = result["issues"]
total_issues += len(issues)
print(f"\n📄 {file_path.relative_to(fixer.project_root)}")
for line_num, line, old_ref, new_ref in issues:
print(f" Line {line_num}: {old_ref}{new_ref}")
print(f" {line.strip()}")
print(f"\n⚠️ Found {total_issues} local action reference issues in {len(results)} files")
print("Run without --check to fix these issues")
return 1
def _run_fix_mode(fixer: LocalActionRefsFixer, *, dry_run: bool) -> int:
"""Run in fix/dry-run mode and return exit code."""
action = (
"🔍 Checking what would be fixed..." if dry_run else "🔧 Fixing local action references..."
)
print(f"{action}")
results = fixer.fix_all_files(dry_run=dry_run)
if not results:
print("✅ No local action reference issues found!")
return 0
total_fixes = 0
for result in results:
if result["error"]:
print(f"❌ Error processing {result['file']}: {result['error']}")
continue
file_path = result["file"]
fixes_made = result["fixes_made"]
total_fixes += fixes_made
if fixes_made > 0:
action_word = "Would fix" if dry_run else "Fixed"
relative_path = file_path.relative_to(fixer.project_root)
print(
f"📄 {action_word} {fixes_made} reference(s) in {relative_path}",
)
if dry_run:
print(f"\n📋 Would fix {total_fixes} local action references in {len(results)} files")
print("Run without --dry-run to apply these fixes")
else:
print(f"\n✅ Fixed {total_fixes} local action references in {len(results)} files")
return 0
def main() -> int:
"""Main entry point."""
parser = _create_argument_parser()
args = parser.parse_args()
fixer = LocalActionRefsFixer()
if args.check:
return _run_check_mode(fixer)
return _run_fix_mode(fixer, dry_run=args.dry_run)
if __name__ == "__main__":
sys.exit(main())