B

GitHub Actions Validator

Comprehensive toolkit for validating, linting, and testing GitHub Actions workflow files, custom local actions, and public actions. Use this skill when working with GitHub Actions YAML files (.github/workflows/*.yml), validating workflow syntax, testing workflow execution with act, or debugging workflow issues.

npx skills add github:pantheon-org/tekhne --skill ci-cd--github-actions--validator

Overview

Validate and test GitHub Actions workflows, custom actions, and public actions using industry-standard tools (actionlint and act). This skill provides comprehensive validation including syntax checking, static analysis, local workflow execution testing, and action verification with version-aware documentation lookup.

Philosophy

  • Lint first, execute second: actionlint is fast and needs no Docker, so it always runs before act.
  • Every reported error maps to a documented fix in references/; quote that fix rather than improvising one.
  • Treat security findings (script injection, unpinned actions) as defects, never as style preferences.
  • Static analysis has limits: state plainly what actionlint and act cannot prove.

When to Use

  • Validating a workflow file under .github/workflows/ before commit or merge
  • Debugging a workflow that fails syntax, expression or schema checks
  • Testing a workflow locally with act before pushing
  • Checking that public actions use current, non-deprecated versions
  • Reviewing a workflow for script injection and other actionlint security findings

When Not to Use

  • Authoring a new workflow from scratch (use the GitHub Actions generator skill instead)
  • Validating CI definitions for other systems such as GitLab CI, Jenkins or Azure Pipelines
  • Diagnosing failures that only occur on GitHub-hosted runners at run time, which static analysis cannot reproduce
  • Validating private actions you cannot access

CRITICAL: Assistant Workflow (MUST FOLLOW)

Every validation MUST follow these steps. Skipping any step is non-compliant.

Step 1: Run Validation Script

cd <skill-directory>  # the folder containing this SKILL.md
bash scripts/validate_workflow.sh <workflow-file-or-directory>

Step 2: For EACH Error - Consult Reference File

When actionlint or act reports ANY error, you MUST:

  1. Read the appropriate reference file (see mapping below)
  2. Find the matching error pattern
  3. Extract the fix/solution

Step 3: Quote the Fix to User

For each error, provide:

  1. Error message (from script output)
  2. Explanation (from reference file)
  3. Fix code (quoted from reference file)
  4. Corrected code (applied to user’s workflow)

Step 4: Verify Public Actions (if present)

For any public actions (uses: owner/action@version):

  1. First check references/action_versions.md for known actions and versions
  2. Use web search for unknown actions: "[action-name] [version] github action documentation"
  3. Verify required inputs match
  4. Check for deprecation warnings

Step 5: Provide Complete Summary

After all errors are addressed:

  • List all fixes applied
  • Note any warnings
  • Recommend best practices from references/

Error-to-Reference Mapping

Error Pattern / Output Category Reference File Section to Consult
runs-on:, runner, ubuntu, macos, windows references/runners.md Runner labels
cron, schedule / [SCHEDULE] references/common_errors.md Schedule Errors
${{, expression, if: / [EXPRESSION] references/common_errors.md Expression Errors
needs:, job, dependency / [SYNTAX] references/common_errors.md Job Configuration Errors
uses:, action, input / [ACTION] references/common_errors.md Action Errors
untrusted, injection, security / [SECURITY] references/common_errors.md Script Injection section
syntax, yaml, unexpected references/common_errors.md Syntax Errors
docker, container / [DOCKER] references/act_usage.md Troubleshooting
[ACT-LIMIT], act fails but GitHub works references/act_usage.md Limitations
@v3, @v4, deprecated, outdated references/action_versions.md Version table
workflow_call, reusable, oidc references/modern_features.md Relevant section
glob, path, paths:, pattern references/common_errors.md Path Filter Errors
User asks about actionlint config references/actionlint_usage.md Provide examples
Runner questions/errors references/runners.md Labels and availability

Quick Start

Initial Setup

cd <skill-directory>  # the folder containing this SKILL.md
bash scripts/install_tools.sh  # Installs act and actionlint to scripts/.tools/

Validation Commands

# Full validation (lint + test)
bash scripts/validate_workflow.sh .github/workflows/ci.yml

# Lint-only (fastest, no Docker required)
bash scripts/validate_workflow.sh --lint-only .github/workflows/ci.yml

# Test-only with act (requires Docker)
bash scripts/validate_workflow.sh --test-only .github/workflows/ci.yml

# Validate all workflows
bash scripts/validate_workflow.sh .github/workflows/

actionlint checks: YAML syntax, schema compliance, expression syntax, runner labels, action inputs/outputs, job dependencies, CRON syntax, glob patterns, shell scripts, security vulnerabilities.

Note: act has limitations: see references/act_usage.md.

Validating Resource Types

Workflows

# Single workflow
bash scripts/validate_workflow.sh .github/workflows/ci.yml

# All workflows
bash scripts/validate_workflow.sh .github/workflows/

Key validation points: triggers, job configurations, runner labels, environment variables, secrets, conditionals, matrix strategies.

Custom Local Actions

Create a test workflow that uses the custom action, then validate:

bash scripts/validate_workflow.sh .github/workflows/test-custom-action.yml

Public Actions

When workflows use public actions (e.g., actions/checkout@v6):

  1. Use web search to find action documentation
  2. Verify required inputs and version
  3. Check for deprecation warnings
  4. Run validation script

Search format: "[action-name] [version] github action documentation"

Reference Files Summary

File Content
references/act_usage.md Act tool usage, commands, options, limitations, troubleshooting
references/actionlint_usage.md Actionlint validation categories, configuration, integration
references/common_errors.md Common errors catalog with fixes
references/action_versions.md Current action versions, deprecation timeline, SHA pinning
references/modern_features.md Reusable workflows, SBOM, OIDC, environments, containers
references/runners.md GitHub-hosted runners (ARM64, GPU, M2 Pro, deprecations)

Troubleshooting

Issue Solution
“Tools not found” Run bash scripts/install_tools.sh
“Docker daemon not running” Start Docker or use --lint-only
“Permission denied” Run chmod +x scripts/*.sh
act fails but GitHub works See references/act_usage.md Limitations

Debug Mode

actionlint -verbose .github/workflows/ci.yml  # Verbose actionlint
act -v                                         # Verbose act
act -n                                         # Dry-run (no execution)

Limitations

  • act limitations: Not all GitHub Actions features work locally
  • Docker requirement: act requires Docker to be running
  • Network actions: Some GitHub API actions may fail locally
  • Private actions: Cannot validate without access
  • Runtime behavior: Static analysis cannot catch all issues
  • File location: act can only validate workflows in .github/workflows/ directory; files outside (like assets/) can only be validated with actionlint

Quick Examples

Example 1: Pre-commit Validation

cd <skill-directory>  # the folder containing this SKILL.md
bash scripts/validate_workflow.sh .github/workflows/
git add .github/workflows/ && git commit -m "Update workflows"

Example 2: Debug Failing Workflow

bash scripts/validate_workflow.sh --lint-only .github/workflows/failing.yml
# Fix issues
bash scripts/validate_workflow.sh .github/workflows/failing.yml

Complete Worked Example: Multi-Error Workflow

User’s Problematic Workflow

name: Broken CI
on:
  schedule:
    - cron: '0 0 * * 8'  # ERROR 1
jobs:
  build:
    runs-on: ubuntu-lastest  # ERROR 2
    steps:
      - uses: actions/checkout@v3  # ERROR 3 (outdated)
      - run: echo ${{ github.event.issue.title }}  # ERROR 4 (security)
  deploy:
    needs: biuld  # ERROR 5 (typo)
    runs-on: ubuntu-latest
    steps:
      - run: echo "Deploying"

Step 1: Run Validation

bash scripts/validate_workflow.sh --lint-only workflow.yml

Output:

[ERROR] invalid CRON format "0 0 * * 8"
[ERROR] label "ubuntu-lastest" is unknown
[WARN] "github.event.issue.title" is potentially untrusted
[ERROR] job "deploy" needs job "biuld" which does not exist

Step 2-3: Consult References and Quote Fixes to User

Using the Error-to-Reference Mapping table above, consult the relevant reference file for each error and quote the fix to the user:

# Error Reference File Fix
1 invalid CRON format "0 0 * * 8" common_errors.md - Schedule Errors Change 8 to 0 (weekday range is 0–6)
2 label "ubuntu-lastest" is unknown common_errors.md + runners.md Change to ubuntu-latest
3 checkout@v3 (outdated) action_versions.md Update to @v6 or SHA-pinned equivalent
4 Untrusted input in run: common_errors.md - Script Injection Pass through environment variable
5 needs: biuld (typo) common_errors.md - Job Configuration Change to needs: build

Step 4: Corrected Workflow

name: Fixed CI
on:
  schedule:
    - cron: '0 0 * * 0'  # Fixed: Sunday (valid range 0-6)
jobs:
  build:
    runs-on: ubuntu-latest  # Fixed: typo corrected
    steps:
      - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3  # v6.0.0
      - name: Process issue
        env:
          TITLE: ${{ github.event.issue.title }}  # Fixed: sanitized via env var
        run: echo "$TITLE"
  deploy:
    needs: build  # Fixed: typo corrected
    runs-on: ubuntu-latest
    steps:
      - run: echo "Deploying"

Step 5: Summary

Error Type Fix Applied
CRON 0 0 * * 8 Schedule Changed to 0 0 * * 0
ubuntu-lastest Runner Changed to ubuntu-latest
checkout@v3 Outdated Action Updated to @v6.0.0 (SHA-pinned)
Direct ${{ }} in run Security Wrapped in environment variable
needs: biuld Job Dependency Changed to needs: build

Recommendations:

  • Run bash scripts/validate_workflow.sh --check-versions regularly
  • Use SHA pinning for all actions in production workflows
  • Always pass untrusted input through environment variables

Anti-Patterns

NEVER skip lint-only mode when Docker is unavailable

WHY: Skipping validation entirely because Docker is not installed leaves syntax errors, schema violations, and security issues undetected. actionlint catches the majority of real errors without Docker.

BAD: Skip all validation when Docker is not installed.

GOOD: Run bash scripts/validate_workflow.sh --lint-only <file>, which covers syntax, schema, and security checks without a container runtime.

NEVER ignore script injection warnings from actionlint

WHY: Script injection warnings flag real attack vectors. Untrusted github.event.* values interpolated directly into shell commands allow a pull request author to execute arbitrary code in your workflow.

BAD: Dismiss script injection warnings as low priority and merge anyway.

GOOD: Pass untrusted input through environment variables (env: PR_TITLE: ${{ github.event.pull_request.title }}) and reference "$PR_TITLE" in the run: step.

NEVER validate a workflow file in isolation when it uses workflow_call or matrix

WHY: Reusable workflows and matrix strategies depend on caller-provided inputs and context that cannot be resolved by validating the file alone. Skipping caller validation misses entire classes of type and expression errors.

BAD: Validate only the reusable workflow file without also validating the calling workflow.

GOOD: Validate both the caller and callee, and note any warnings that need runtime context that static analysis cannot resolve.

NEVER accept deprecation warnings as harmless

WHY: Deprecated action major versions (for example @v2 when @v4 is current) may receive no security patches. A known vulnerability in a deprecated version is an open door into your CI environment.

BAD: Leave actions/checkout@v2 in place after the validator warns it is outdated.

GOOD: Update to the current SHA-pinned version listed in references/action_versions.md.

NEVER fix an error without consulting the reference file first

WHY: The workflow requires each actionlint or act error to be matched against references/ and the documented fix quoted to the user. Guessing a fix risks a change that silences the message but leaves the underlying defect.

BAD: See label "ubuntu-lastest" is unknown and edit the workflow without checking references/runners.md.

GOOD: Look the error up in the Error-to-Reference Mapping, quote the explanation and fix, then apply it.

NEVER use web search for an action that references/action_versions.md already covers

WHY: The reference file is the first source of truth for known actions and versions. Searching first wastes effort and can surface stale or unofficial documentation.

BAD: Search the web for actions/checkout documentation before opening references/action_versions.md.

GOOD: Check references/action_versions.md first and search the web only for actions it does not list.

NEVER treat a passing act run as proof the workflow works on GitHub

WHY: act has documented limitations, and some GitHub API actions fail or behave differently locally. A local pass or fail is a signal, not a guarantee.

BAD: Report a workflow as verified because act succeeded, or as broken because act failed while actionlint is clean.

GOOD: Combine the actionlint result with act, and consult the Limitations section of references/act_usage.md before concluding either way.

NEVER run act against files outside .github/workflows/

WHY: act can only validate workflows in the .github/workflows/ directory. Files elsewhere, such as under assets/, can only be checked with actionlint.

BAD: Run bash scripts/validate_workflow.sh --test-only assets/valid-ci.yml and read the result as a pass.

GOOD: Use --lint-only for files outside .github/workflows/.

NEVER leave actions unpinned in production workflows

WHY: Mutable tags such as @v3 can change underneath you. The recommended practice in this skill is SHA pinning for production workflows, with the version noted in a comment.

BAD: uses: actions/checkout@v3

GOOD: uses: actions/checkout@<full-commit-sha> # v6.0.0, with the SHA taken from references/action_versions.md.

Summary

  1. Setup: Install tools with install_tools.sh
  2. Validate: Run validate_workflow.sh on workflow files
  3. Fix: Address issues using reference documentation
  4. Test: Verify locally with act (when possible)
  5. Search: Use web search to verify unknown actions
  6. Commit: Push validated workflows with confidence

References

Topic Reference When to Use
actionlint error patterns with explanations and fixes Common Errors Diagnosing a specific actionlint error message
Runner labels, availability, and hardware specs Runners Choosing or validating a runs-on label
Current version table for popular actions with SHA pins Action Versions Updating a deprecated or unpinned action reference
Local workflow testing with act, limitations and workarounds act Usage Running workflows locally before pushing
actionlint configuration and custom rule examples actionlint Usage Configuring actionlint or adding custom ignore rules
Modern GHA features: job summaries, environments, containers Modern Features Validating workflows that use newer GitHub Actions APIs