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--validatorOverview
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:
actionlintis fast and needs no Docker, so it always runs beforeact. - 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
actionlintandactcannot 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
actbefore pushing - Checking that public actions use current, non-deprecated versions
- Reviewing a workflow for script injection and other
actionlintsecurity 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:
- Read the appropriate reference file (see mapping below)
- Find the matching error pattern
- Extract the fix/solution
Step 3: Quote the Fix to User
For each error, provide:
- Error message (from script output)
- Explanation (from reference file)
- Fix code (quoted from reference file)
- Corrected code (applied to user’s workflow)
Step 4: Verify Public Actions (if present)
For any public actions (uses: owner/action@version):
- First check
references/action_versions.mdfor known actions and versions - Use web search for unknown actions:
"[action-name] [version] github action documentation" - Verify required inputs match
- 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):
- Use web search to find action documentation
- Verify required inputs and version
- Check for deprecation warnings
- 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 (likeassets/) 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-versionsregularly - 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
- Setup: Install tools with
install_tools.sh - Validate: Run
validate_workflow.shon workflow files - Fix: Address issues using reference documentation
- Test: Verify locally with act (when possible)
- Search: Use web search to verify unknown actions
- 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 |
Explicit NEVER/WHY/consequence triplets with side-by-side bad/good examples. Generic warnings score low — concrete failure modes with real impact (security, correctness, performance) score high.
Runtime validation via eval scenarios proving the skill actually changes agent behaviour. Requires evals/ directory, instructions.json, ≥3 scenarios, and ≥80% instruction coverage in summary.json.
Appropriate rigidity for the skill type: strict rules for critical/security skills, balanced steps for process skills, flexible options for tool skills. Mismatched calibration (too rigid or too loose) reduces score.
Expert-only knowledge the AI couldn't derive on its own — domain-specific patterns, production gotchas, decision frameworks, and non-obvious tool behaviour. High scores mean <5% redundancy with what the model already knows.
A clear philosophy plus numbered step-by-step workflows with explicit entry/exit points. Should answer: what is the core principle, how to execute it, and when (and when not) to apply it.
Rich activation keywords and trigger scenarios in the description so the skill fires when needed. Best descriptions read like an exhaustive list of the tasks and concepts that should invoke this skill.
Copy-paste-ready code, complete runnable examples, real file paths and commands. Pseudocode and abstract descriptions score low — concrete, executable content scores high.
SKILL.md as a <100-line navigation hub pointing to focused references/ files. Content should be lazy-loaded on demand, not front-loaded. Includes actionable 'When to Use' conditions per reference.
Proper frontmatter, single-task focus, activation keywords, and cross-harness portability. Skills must work across 40+ agentic harnesses without hardcoded paths or agent-specific references.
Expert-only knowledge the AI couldn't derive on its own — domain-specific patterns, production gotchas, decision frameworks, and non-obvious tool behaviour. High scores mean <5% redundancy with what the model already knows.
A clear philosophy plus numbered step-by-step workflows with explicit entry/exit points. Should answer: what is the core principle, how to execute it, and when (and when not) to apply it.
Explicit NEVER/WHY/consequence triplets with side-by-side bad/good examples. Generic warnings score low — concrete failure modes with real impact (security, correctness, performance) score high.
Proper frontmatter, single-task focus, activation keywords, and cross-harness portability. Skills must work across 40+ agentic harnesses without hardcoded paths or agent-specific references.
SKILL.md as a <100-line navigation hub pointing to focused references/ files. Content should be lazy-loaded on demand, not front-loaded. Includes actionable 'When to Use' conditions per reference.
Appropriate rigidity for the skill type: strict rules for critical/security skills, balanced steps for process skills, flexible options for tool skills. Mismatched calibration (too rigid or too loose) reduces score.
Rich activation keywords and trigger scenarios in the description so the skill fires when needed. Best descriptions read like an exhaustive list of the tasks and concepts that should invoke this skill.
Copy-paste-ready code, complete runnable examples, real file paths and commands. Pseudocode and abstract descriptions score low — concrete, executable content scores high.
No eval scenarios available.
Act (nektos/act) - Usage Reference
Act is a tool that allows you to run your GitHub Actions locally, providing fast feedback and acting as a local task runner.
Installation
# Install act using the official script
curl --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/nektos/act/master/install.sh | bash
# Or use the skill's installation script
bash scripts/install_tools.sh
Core Commands
List Workflows
List all available workflows in the repository:
act -l
List workflows for a specific event:
act -l pull_request
act -l push
act -l workflow_dispatch
Dry Run (No Execution)
Validate workflows without executing them, useful for inspection and validation:
act -n
# or (long form)
act --dryrun
This performs a dry run that:
- Parses all workflow files
- Validates syntax
- Shows what would be executed
- Does NOT actually run any jobs
- Returns exit code 0 on success, non-zero on errors
Important for Validation: The dry-run mode is perfect for validating workflow syntax before pushing to GitHub.
Run Workflows
Run the default workflow:
act
Run workflows for a specific event:
act push
act pull_request
act workflow_dispatch
Run a specific job:
act -j <job-id>
Run a specific workflow file:
act -W .github/workflows/ci.yml
Common Use Cases
1. Validate Workflow Syntax
Use dry run to check if workflows parse correctly:
act -n
If there are syntax errors, act will report them immediately.
2. Test Workflows Locally
Before pushing to GitHub, test workflows locally:
# Test push event workflows
act push
# Test pull request workflows
act pull_request
3. Debug Workflow Issues
Run workflows with verbose output:
act -v
4. List Available Events
See which events have workflows configured:
act -l
Output format:
Stage Job ID Job name Workflow name Workflow file Events
0 build build CI ci.yml push,pull_request
0 test test CI ci.yml push,pull_request
Advanced Options
Container Architecture
Ensure consistent platform behavior across different machines:
act --container-architecture linux/amd64
This is especially important on ARM-based Macs (M1/M2/M3) to ensure workflows run in the same environment as GitHub’s x64 runners.
Using Specific Docker Images
Act uses Docker containers to run jobs. Specify custom images:
act -P ubuntu-latest=node:16-buster
Configuration File
Create .actrc file in your project or home directory to set default options:
# .actrc
--container-architecture=linux/amd64
--action-offline-mode
Options are loaded in this order:
- XDG spec
.actrc - HOME directory
.actrc - Current directory
.actrc - CLI arguments
Passing Secrets
Provide secrets for testing:
act -s GITHUB_TOKEN=ghp_xxx
Or use a secrets file:
act --secret-file .secrets
Environment Variables
Set environment variables:
act --env MY_VAR=value
Input Variables (for workflow_dispatch)
Pass input variables:
act workflow_dispatch --input myInput=myValue
Limitations
Be aware of act’s limitations:
- Not 100% Compatible: Some GitHub Actions features may not work exactly as on GitHub
- Docker Required: act requires Docker to be installed and running
- Network Actions: Some actions that interact with GitHub’s API may fail
- Runner Images: Default runner images may differ from GitHub’s hosted runners
- Secrets: Local testing requires manually providing secrets
Exit Codes
0: Success - all jobs passed1: Failure - at least one job failed2: Error - workflow parsing or execution error
Best Practices for Validation
- Always run dry-run first:
act -nto catch syntax errors - Test specific events: Don’t run all workflows, target the event you care about
- Use verbose mode for debugging:
act -vwhen troubleshooting - Check Docker availability: Ensure Docker is running before using act
- Consider limitations: Not all features work locally - use for syntax and basic logic validation
Troubleshooting
Issue: “Cannot connect to Docker daemon”
Solution: Start Docker Desktop or Docker daemon
Issue: “Workflow file not found”
Solution: Ensure you’re in the repository root or use -W to specify the workflow file path
Issue: “Action not found”
Solution: Some actions may not be available locally. Use -P to specify alternative Docker images or skip the problematic action for validation purposes
Issue: “Out of disk space”
Solution: Clean up Docker images: docker system prune -a
Action Version Validation Reference
This reference provides current recommended action versions and validation procedures for GitHub Actions workflows.
Current Recommended Versions (December 2025)
| Action | Current Version | Minimum Supported | Notes |
|---|---|---|---|
actions/checkout |
v6 | v4 | v6 stores credentials in $RUNNER_TEMP |
actions/setup-node |
v6 | v4 | v6 adds Node 24 support |
actions/setup-python |
v5 | v4 | v5 adds Python 3.13 support |
actions/setup-java |
v4 | v4 | Current latest |
actions/setup-go |
v5 | v4 | v5 adds Go 1.23 support |
actions/cache |
v4 | v4 | v4.2.0+ required as of Feb 2025 |
actions/upload-artifact |
v4 | v4 | v3 deprecated |
actions/download-artifact |
v4 | v4 | v3 deprecated |
docker/setup-buildx-action |
v3 | v3 | Current latest |
docker/login-action |
v3 | v3 | Current latest |
docker/build-push-action |
v6 | v5 | v6 adds provenance attestation |
docker/metadata-action |
v5 | v5 | Current latest |
aws-actions/configure-aws-credentials |
v4 | v4 | OIDC support improved |
Version Validation Process
Step 1: Extract Action References
For each uses: statement in the workflow, extract:
- Action name (e.g.,
actions/checkout) - Version (e.g.,
v4,v4.1.1, or SHA likeb4ffde65f46...)
Step 2: Compare Against Recommended Versions
For each action found:
- Look up the action in the table above
- Compare the workflow version against the Current Version
- Flag if using a version older than Minimum Supported
Step 3: Report Findings
Generate warnings for:
- OUTDATED: Action using older major version (e.g., checkout@v4 when v6 is current)
- DEPRECATED: Action using version below minimum supported
- UP-TO-DATE: Action using current or acceptable version
Example Version Validation Output
=== Action Version Check ===
actions/checkout@v6.0.0 - UP-TO-DATE (current: v6)
actions/setup-java@v4.2.1 - UP-TO-DATE (current: v4)
docker/build-push-action@v5.3.0 - OUTDATED (current: v6, using: v5)
actions/upload-artifact@v3 - DEPRECATED (minimum: v4, using: v3)
Recommendation: Update docker/build-push-action to v6 for provenance attestation support
Recommendation: Update actions/upload-artifact to v4 (v3 is deprecated)
Using the Version Check Flag
# Check action versions in workflow
bash scripts/validate_workflow.sh --check-versions .github/workflows/ci.yml
# Full validation including version check
bash scripts/validate_workflow.sh .github/workflows/ci.yml
Node.js Runtime Deprecation Timeline
GitHub Actions runtime requirements:
- Node.js 12: EOL April 2022 - Actions using this are deprecated
- Node.js 16: EOL September 2023 - Actions using this are deprecated
- Node.js 20: EOL April 2026 - Current runtime for most actions
- Node.js 22/24: Current LTS - Newer actions support these
SHA Pinning Best Practice
For security, pin actions to specific commit SHAs:
# Recommended: SHA pinning with version comment
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
# Acceptable: Major version tag
- uses: actions/checkout@v6
# Not recommended: Branch reference
- uses: actions/checkout@main
Cache Storage Updates (November 2025)
GitHub Actions cache storage expanded beyond the 10 GB limit:
New Features:
- Pay-as-you-go model: Repositories can store more than 10 GB of cache data
- Free tier: All repositories continue to receive 10 GB at no additional cost
- New management policies:
- Cache size eviction limit (GB): Control maximum cache size
- Cache retention limit (days): Set how long caches are retained
Pricing:
- First 10 GB per repository: FREE
- Additional storage: Comparable to Git LFS and Codespaces storage
- Requires Pro, Team, or Enterprise account to exceed 10 GB limit
Cache best practices:
- Monitor cache usage in repository settings
- Configure eviction limits to control costs
- Use appropriate retention periods for your workflow
- Clean up old caches regularly
- Consider cache key strategies to avoid cache bloat
Validation Checklist
When validating workflows, ALWAYS:
- Run the validation script
- Manually review
uses:statements against the version table - Warn about any outdated or deprecated versions
- Suggest specific upgrade paths with SHA pinning
Actionlint (rhysd/actionlint) - Usage Reference
Actionlint is a static checker for GitHub Actions workflow files that catches errors before they cause CI failures.
Installation
# Download and install using the official script
bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash)
# Or use the skill's installation script
bash scripts/install_tools.sh
Core Usage
Basic Validation
Validate a single workflow file:
actionlint .github/workflows/ci.yml
Validate all workflow files in a directory:
actionlint .github/workflows/*.yml
Validate all workflows in the default location:
actionlint
Output Formats
Default Format (human-readable)
actionlint
Output example:
.github/workflows/ci.yml:5:7: unexpected key "job" for "workflow" section [syntax-check]
.github/workflows/ci.yml:10:15: invalid CRON format "0 0 * * 8" in schedule event [events]
JSON Format
actionlint -format '{{json .}}'
Useful for programmatic processing and integration with other tools.
Sarif Format
actionlint -format sarif
For integration with GitHub Code Scanning and other security tools.
Validation Categories
1. Syntax Checking
Validates YAML syntax and GitHub Actions schema:
- Required fields
- Valid keys and values
- Proper nesting
- Type correctness
2. Expression Validation
Validates GitHub Actions expressions ${{ }}:
- Syntax errors
- Type checking (string, number, boolean)
- Function calls
- Context access
Example caught errors:
# Error: Boolean expression expected
if: ${{ 'true' }} # String, not boolean
# Error: Unknown function
run: echo ${{ unknown() }}
# Error: Type mismatch
if: ${{ 42 }} # Number, not boolean
3. Runner Label Validation
Validates runner labels against known GitHub-hosted runners:
Ubuntu:
ubuntu-latest(currently ubuntu-24.04)ubuntu-24.04,ubuntu-22.04,ubuntu-20.04
Windows:
windows-latest(currently windows-2022)windows-2025(NEW - recently added)windows-2022,windows-2019
macOS:
macos-latest(currently macos-15)macos-15(Apple Silicon M1/M2/M3)macos-14(Apple Silicon M1)macos-26(preview)macos-13(Intel - RETIRED November 14, 2025)macos-12(Intel - RETIRED)
Example:
runs-on: ubuntu-lastest # Error: Did you mean "ubuntu-latest"?
4. Action Validation
Validates action references:
- Action exists
- Valid version/ref
- Required inputs provided
- No unknown inputs
Example:
# Error: Missing required input "path"
- uses: actions/checkout@v5
# Error: Unknown input "invalid_input"
- uses: actions/checkout@v5
with:
invalid_input: value
5. Job Dependencies
Validates needs: dependencies:
- Referenced jobs exist
- No circular dependencies
- Valid job IDs
6. CRON Syntax
Validates schedule event CRON expressions:
# Error: Day of week must be 0-6
schedule:
- cron: '0 0 * * 8'
7. Shell Script Validation
Integrates with shellcheck to validate shell scripts in run: steps:
# Warning: Quote to prevent word splitting
run: echo $VARIABLE
8. Glob Pattern Validation
Validates glob patterns in path filters:
on:
push:
paths:
- '**.js' # Error: Should be '**/*.js'
9. Security Checks
Detects potential security issues:
- Injection vulnerabilities
- Insecure credential handling
- Dangerous patterns
Example:
# Warning: Potential script injection
run: echo ${{ github.event.issue.title }}
Configuration
Create .github/actionlint.yaml or .github/actionlint.yml:
# Configure shellcheck
shellcheck:
enable: true
shell: bash
# Configure pyflakes for Python
pyflakes:
enable: true
executable: pyflakes
# Ignore specific rules
ignore:
- 'SC2086' # Ignore shellcheck rule
- 'action-validation' # Ignore action validation
# Custom runner labels
self-hosted-runner:
labels:
- my-custom-runner
- gpu-runner
Exit Codes
0: Success - no errors found1: Validation errors found2: Fatal error (invalid file, config error, etc.)
Integration
Pre-commit Hook
# .pre-commit-config.yaml
repos:
- repo: https://github.com/rhysd/actionlint
rev: v1.7.9 # Check https://github.com/rhysd/actionlint/releases for latest version
hooks:
- id: actionlint
Note: Always use the latest version of actionlint. Check the releases page for the most recent version.
GitHub Actions Workflow
name: Lint GitHub Actions workflows
on: [push, pull_request]
jobs:
actionlint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- name: Download actionlint
run: bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash)
- name: Run actionlint
run: ./actionlint
VS Code Integration
Install the “actionlint” extension for real-time validation in VS Code.
Common Error Examples
1. Typo in Runner Label
# Error
runs-on: ubuntu-lastest
# Fix
runs-on: ubuntu-latest
2. Invalid CRON Expression
# Error
schedule:
- cron: '0 0 * * 8' # Day of week 8 doesn't exist
# Fix
schedule:
- cron: '0 0 * * 0' # Sunday = 0
3. Missing Required Input
# Error
- uses: actions/checkout@v4
# Fix (if repository input is required)
- uses: actions/checkout@v4
with:
repository: owner/repo
4. Invalid Expression
# Error
if: ${{ success() && 'true' }} # Mixing boolean and string
# Fix
if: ${{ success() && true }}
5. Undefined Job in needs
# Error
jobs:
deploy:
needs: biuld # Typo
# Fix
jobs:
deploy:
needs: build
Best Practices
- Run locally before pushing: Catch errors early
- Use in CI/CD: Add actionlint to your workflow
- Configure for custom runners: Update config for self-hosted runners
- Enable shellcheck: Catch shell script issues
- Review all warnings: Even non-fatal warnings can indicate issues
- Keep actionlint updated: New rules and features are added regularly
Limitations
- Cannot validate runtime behavior (only static analysis)
- Cannot access private actions (must be public to validate)
- May not catch all possible issues (e.g., environment-specific problems)
- Custom actions may require manual verification
Common GitHub Actions Errors and Solutions
This reference lists common errors encountered when working with GitHub Actions and how to fix them.
Syntax Errors
1. Invalid YAML Syntax
Error:
Error: Unable to process file command 'workflow' successfully.
Common Causes:
- Incorrect indentation (YAML is whitespace-sensitive)
- Missing colons
- Unquoted strings containing special characters
- Tabs instead of spaces
Fix:
# Bad
name:My Workflow
jobs:
build:
runs-on: ubuntu-latest
# Good
name: My Workflow
jobs:
build:
runs-on: ubuntu-latest
2. Missing Required Fields
Error:
Required property is missing: name
Fix:
# Every workflow needs a name
name: CI Pipeline
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
3. Invalid Workflow Triggers
Error:
The workflow is not valid. Unexpected value 'on'
Fix:
# Bad - wrong event name
on:
pull-request: # Should be pull_request
# Good
on:
pull_request:
push:
Expression Errors
1. Incorrect Expression Syntax
Error:
Unrecognized named-value: 'github'. Located at position 1 within expression: github.ref
Fix:
# Bad - missing ${{ }}
if: github.ref == 'refs/heads/main'
# Good
if: ${{ github.ref == 'refs/heads/main' }}
# Even better (GitHub Actions auto-evaluates if conditions)
if: github.ref == 'refs/heads/main'
2. Type Mismatches
Error:
Expected boolean value, got string
Fix:
# Bad
if: ${{ 'true' }} # String, not boolean
# Good
if: ${{ true }}
if: ${{ success() }}
if: ${{ github.event_name == 'push' }}
3. Script Injection Vulnerabilities
Warning:
Potential script injection via untrusted input
Fix:
# Bad - vulnerable to injection
run: echo ${{ github.event.issue.title }}
# Good - use environment variables
env:
TITLE: ${{ github.event.issue.title }}
run: echo "$TITLE"
Action Errors
1. Action Not Found
Error:
Can't find 'action.yml', 'action.yaml' or 'Dockerfile' under '/home/runner/work/_actions/actions/chekout/v4'
Common Causes:
- Typo in action name
- Invalid action reference
- Action doesn’t exist or was removed
Fix:
# Bad
- uses: actions/chekout@v4 # Typo
# Good
- uses: actions/checkout@v4
2. Missing Required Inputs
Error:
Input required and not supplied: path
Fix:
# Bad
- uses: some-action@v1
# Good
- uses: some-action@v1
with:
path: ./my-path
3. Unknown Action Inputs
Error:
Unexpected input 'invalid_input'
Fix:
# Check the action's documentation for valid inputs
- uses: actions/checkout@v4
with:
# Only use documented inputs
ref: main
# Remove undocumented inputs
4. Deprecated Action Versions
Warning:
Node.js 12/16 actions are deprecated
Fix:
# Deprecated - Node.js 12 (EOL April 2022)
- uses: actions/checkout@v2
# Deprecated - Node.js 16 (EOL September 2023)
- uses: actions/checkout@v3
# Older - Node.js 20 (EOL April 2026)
- uses: actions/checkout@v4
- uses: actions/checkout@v5
# Current - Node.js 20+/24 (v6)
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
# Note: Node.js 20 EOL is April 2026, Node.js 22 and 24 are current
Job Configuration Errors
1. Invalid Runner Label
Error:
Unable to locate executable file: ubuntu-lastest
Fix:
# Bad
runs-on: ubuntu-lastest # Typo
# Good
runs-on: ubuntu-latest
Valid runner labels:
ubuntu-latest,ubuntu-22.04,ubuntu-20.04windows-latest,windows-2025,windows-2022,windows-2019macos-latest(now macOS 15),macos-15,macos-14,macos-26(preview)macos-13(RETIRED November 14, 2025 - no longer available)macos-15-intel,macos-15-large(Intel x86_64, long-term deprecated)macos-15-xlarge,macos-14-xlarge(M2 Pro with GPU)gpu-t4-4-core(GPU runners for ML/AI)- ARM64 runners (free for public repos)
2. Undefined Job Dependency
Error:
Job 'deploy' depends on job 'biuld' which does not exist
Fix:
# Bad
jobs:
build:
runs-on: ubuntu-latest
deploy:
needs: biuld # Typo
# Good
jobs:
build:
runs-on: ubuntu-latest
deploy:
needs: build
3. Circular Job Dependencies
Error:
Circular dependency detected
Fix:
# Bad
jobs:
job1:
needs: job2
job2:
needs: job1 # Circular!
# Good
jobs:
job1:
runs-on: ubuntu-latest
job2:
needs: job1
Schedule Errors
1. Invalid CRON Syntax
Error:
Invalid CRON expression: '0 0 * * 8'
Fix:
# Bad
schedule:
- cron: '0 0 * * 8' # Day 8 doesn't exist
# Good
schedule:
- cron: '0 0 * * 0' # Sunday
# CRON format: minute hour day month weekday
# Minute: 0-59
# Hour: 0-23
# Day: 1-31
# Month: 1-12
# Weekday: 0-6 (0 = Sunday)
2. Multiple Schedule Entries
# Correct way to define multiple schedules
on:
schedule:
- cron: '0 0 * * 1' # Monday at midnight
- cron: '0 12 * * 5' # Friday at noon
Path Filter Errors
1. Invalid Glob Pattern
Error:
Invalid glob pattern: '**.js'
Fix:
# Bad
on:
push:
paths:
- '**.js' # Missing directory separator
# Good
on:
push:
paths:
- '**/*.js'
- 'src/**'
Environment and Secrets
1. Secret Not Found
Error:
Secret MY_SECRET not found
Fix:
- Ensure the secret is defined in repository settings
- Check secret name spelling (case-sensitive)
- Verify secret scope (repository vs organization vs environment)
# Use secrets correctly
env:
API_KEY: ${{ secrets.MY_SECRET }} # Must match name in settings
2. Environment Variables in run
Common Issue:
# Bad - environment variable not accessible
steps:
- run: echo $MY_VAR # May not work on Windows
# Good - use env
steps:
- name: Print variable
env:
MY_VAR: ${{ secrets.MY_SECRET }}
run: echo "$MY_VAR" # Unix
# or
run: echo $env:MY_VAR # Windows PowerShell
Matrix Strategy Errors
1. Invalid Matrix Configuration
Error:
Matrix configuration is invalid
Fix:
# Bad
strategy:
matrix:
os: ubuntu-latest # Should be an array
# Good
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node: [20, 22, 24] # Node 16 EOL Sep 2023, Node 20 EOL Apr 2026
2. Matrix Variable Reference
# Correct way to reference matrix variables
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
jobs:
test:
runs-on: ${{ matrix.os }}
Conditional Execution Errors
1. Always/Cancelled/Failure Conditions
# Understanding conditions
steps:
- name: Run on success (default)
run: echo "Runs only if previous steps succeeded"
- name: Run always
if: always()
run: echo "Runs whether previous steps succeeded or failed"
- name: Run on failure
if: failure()
run: echo "Runs only if a previous step failed"
- name: Run on success
if: success()
run: echo "Runs only if all previous steps succeeded"
Debugging Tips
1. Enable Debug Logging
Set secrets in repository settings:
ACTIONS_STEP_DEBUG=true(detailed step logs)ACTIONS_RUNNER_DEBUG=true(runner diagnostic logs)
2. Use tmate for Interactive Debugging
steps:
- name: Setup tmate session
if: failure()
uses: mxschmitt/action-tmate@v3
3. Print Context Information
steps:
- name: Dump GitHub context
run: echo '${{ toJSON(github) }}'
- name: Dump job context
run: echo '${{ toJSON(job) }}'
- name: Dump runner context
run: echo '${{ toJSON(runner) }}'
Best Practices
- Always use specific action versions:
actions/checkout@v6notactions/checkout@main - Quote strings with special characters:
name: "My: Workflow" - Use shellcheck: Enable shell script linting
- Validate locally: Use act and actionlint before pushing
- Use env for secrets: Never put secrets directly in run commands
- Keep workflows DRY: Use reusable workflows and composite actions
- Set timeouts: Prevent runaway jobs with
timeout-minutes - Use concurrency: Cancel redundant runs with concurrency groups
# Example of good practices
name: Production Deployment
on:
push:
branches: [main]
concurrency:
group: production
cancel-in-progress: false
jobs:
deploy:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- name: Deploy
env:
API_KEY: ${{ secrets.API_KEY }}
run: |
./deploy.sh
Modern GitHub Actions Features Reference
This reference covers validation of modern GitHub Actions features including reusable workflows, attestations, OIDC authentication, and more.
Reusable Workflows
Validation Points
workflow_calltrigger configuration- Required and optional inputs with correct types
- Secrets declaration and usage
- Outputs definition
Example
# Reusable workflow (.github/workflows/reusable-deploy.yml)
on:
workflow_call:
inputs:
environment:
required: true
type: string
deploy-version:
required: false
type: string
default: 'latest'
secrets:
deploy-token:
required: true
outputs:
deployment-url:
description: "The URL of the deployment"
value: ${{ jobs.deploy.outputs.url }}
jobs:
deploy:
runs-on: ubuntu-latest
outputs:
url: ${{ steps.deploy.outputs.url }}
steps:
- name: Deploy
id: deploy
run: echo "url=https://example.com" >> $GITHUB_OUTPUT
Common Errors
- Incorrect input types (string, number, boolean)
- Missing required secrets
- Invalid output references
Workflow Limits (November 2025)
GitHub Actions increased reusable workflow limits:
- Nested workflows: Up to 10 levels (previously 4)
- Total workflows per run: Up to 50 workflows (previously 20)
This enables complex workflow compositions and better code reuse.
SBOM and Build Provenance Attestations
Validation Points
- Correct permissions (
id-token: write,attestations: write) - Valid artifact paths
- Proper attestation action usage
Example
permissions:
id-token: write
contents: read
attestations: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Build artifact
run: |
mkdir -p dist
tar -czvf dist/app.tar.gz ./src
- name: Generate SBOM
run: |
# Generate SPDX SBOM
syft ./src -o spdx-json > sbom.spdx.json
- uses: actions/attest-sbom@v3
with:
subject-path: '${{ github.workspace }}/dist/*.tar.gz'
sbom-path: '${{ github.workspace }}/sbom.spdx.json'
- uses: actions/attest-build-provenance@v3
with:
subject-path: '${{ github.workspace }}/dist/*.tar.gz'
Common Errors
- Missing required permissions
- Invalid subject-path glob patterns
- Incorrect SBOM format
OIDC Authentication
Validation Points
- Correct permissions (
id-token: write) - Valid audience claims
- Proper OIDC provider configuration
- Token claim validation in receiving systems
Available Token Claims (November 2025)
| Claim | Description |
|---|---|
repository |
Repository name |
ref |
Git ref (branch/tag) |
sha |
Commit SHA |
workflow |
Workflow name |
run_id |
Workflow run ID |
run_attempt |
Attempt number |
check_run_id |
NEW - Specific check run ID for the job |
actor |
User who triggered the workflow |
environment |
Deployment environment (if applicable) |
Example: AWS OIDC
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsRole
aws-region: us-east-1
# Token now includes check_run_id for granular tracking
- name: Deploy to AWS
run: aws s3 sync ./build s3://my-bucket/
AWS IAM Policy with check_run_id
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:sub": "repo:org/repo:ref:refs/heads/main",
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
},
"StringLike": {
"token.actions.githubusercontent.com:check_run_id": "*"
}
}
}]
}
Benefits of check_run_id
- Fine-grained access control: Trace tokens to exact job and compute
- Improved auditability: Track which specific check run made API calls
- Least-privilege policies: Attribute-based access control without enumerating repositories
- Faster revocation: Reduce secret exposure risk
Deployment Environments
Validation Points
- Environment name configuration
- Protection rules compatibility
- Required reviewers setup
- Environment variables and secrets scope
Example
jobs:
deploy-staging:
runs-on: ubuntu-latest
environment:
name: staging
url: https://staging.example.com
steps:
- uses: actions/checkout@v6
- run: ./deploy.sh staging
deploy-production:
runs-on: ubuntu-latest
needs: deploy-staging
environment:
name: production
url: https://prod.example.com
steps:
- uses: actions/checkout@v6
- run: ./deploy.sh production
Common Errors
- Undefined environment names
- Missing URL for environment tracking
- Incorrect environment variable scope
Job Summaries
Validation Points
- Correct usage of
$GITHUB_STEP_SUMMARY - Valid Markdown formatting
- Proper escaping of dynamic content
Example
steps:
- name: Run tests
id: tests
run: |
# Run tests and capture results
npm test 2>&1 | tee test-output.txt
PASSED=$(grep -c "PASS" test-output.txt || echo 0)
FAILED=$(grep -c "FAIL" test-output.txt || echo 0)
echo "passed=$PASSED" >> $GITHUB_OUTPUT
echo "failed=$FAILED" >> $GITHUB_OUTPUT
- name: Generate summary
run: |
echo "## Test Results" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Status | Count |" >> $GITHUB_STEP_SUMMARY
echo "|--------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| Passed | ${{ steps.tests.outputs.passed }} |" >> $GITHUB_STEP_SUMMARY
echo "| Failed | ${{ steps.tests.outputs.failed }} |" >> $GITHUB_STEP_SUMMARY
Note: Job summaries are runtime features - actionlint validates script syntax but not summary content.
Container Jobs
Validation Points
- Valid container image references
- Correct volume mounts
- Environment variable configuration
- Service container networking
Example
jobs:
test:
runs-on: ubuntu-latest
container:
image: node:24
env:
NODE_ENV: test
volumes:
- /data:/data
services:
postgres:
image: postgres:16
env:
POSTGRES_PASSWORD: postgres
POSTGRES_DB: testdb
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
redis:
image: redis:7
ports:
- 6379:6379
steps:
- uses: actions/checkout@v6
- name: Install dependencies
run: npm ci
- name: Run tests
env:
DATABASE_URL: postgres://postgres:postgres@postgres:5432/testdb
REDIS_URL: redis://redis:6379
run: npm test
Common Errors
- Invalid image tags
- Incorrect volume mount syntax
- Service container networking issues
- Missing health checks for services
Matrix Strategies
Validation Points
- Matrix values must be arrays
- Valid matrix variable references
- Proper include/exclude syntax
Example
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node: [20, 22, 24]
exclude:
- os: macos-latest
node: 20
include:
- os: ubuntu-latest
node: 24
experimental: true
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node }}
- run: npm test
Concurrency Control
Validation Points
- Valid concurrency group names
- Proper cancel-in-progress usage
Example
name: CI
on:
push:
branches: [main]
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- run: npm ci && npm run build
This prevents redundant runs while protecting main branch runs from cancellation.
GitHub-Hosted Runners Reference (2025)
This reference covers all GitHub-hosted runner types, including recent additions and deprecations.
Standard Runner Labels
Ubuntu
runs-on: ubuntu-latest # Ubuntu 24.04 (default)
runs-on: ubuntu-24.04 # Ubuntu 24.04
runs-on: ubuntu-22.04 # Ubuntu 22.04
runs-on: ubuntu-20.04 # Ubuntu 20.04
Windows
runs-on: windows-latest # Windows Server 2022 (default)
runs-on: windows-2025 # Windows Server 2025 (NEW)
runs-on: windows-2022 # Windows Server 2022
runs-on: windows-2019 # Windows Server 2019
macOS
runs-on: macos-latest # macOS 15 (as of Aug 2025)
runs-on: macos-15 # macOS 15 Sequoia (Apple Silicon)
runs-on: macos-14 # macOS 14 Sonoma (Apple Silicon)
runs-on: macos-26 # macOS 26 (PREVIEW)
macOS Runner Updates and Deprecations
Current Status
| Label | Status | Architecture | Notes |
|---|---|---|---|
macos-latest |
Active | ARM64 (Apple Silicon) | Points to macOS 15 |
macos-15 |
Active | ARM64 (Apple Silicon) | M1/M2/M3 |
macos-14 |
Active | ARM64 (Apple Silicon) | M1/M2 |
macos-26 |
Preview | ARM64 (Apple Silicon) | Beta |
macos-13 |
RETIRED | Intel x86_64 | Retired November 14, 2025 |
macos-12 |
RETIRED | Intel x86_64 | Retired |
Intel-Specific Labels (Long-term Deprecated)
runs-on: macos-15-intel # Intel x86_64 (NEW but deprecated long-term)
runs-on: macos-14-large # Intel x86_64
runs-on: macos-15-large # Intel x86_64
Important: Apple Silicon (ARM64) will be required after Fall 2027. Plan migration now.
Migration Example
jobs:
build:
# BAD - macos-13 retired Nov 14, 2025 (WILL FAIL)
# runs-on: macos-13
# GOOD - Use macos-15 or macos-latest
runs-on: macos-15
steps:
- uses: actions/checkout@v6
- run: ./build.sh
ARM64 Runners
Availability
- Generally available as of August 2025
- Free for public repositories
- Private repositories require GitHub Enterprise Cloud plan
Labels
runs-on: ubuntu-latest-arm64 # Free for public repos
runs-on: ubuntu-24.04-arm64
runs-on: windows-latest-arm64 # ARM Windows
Example
jobs:
build:
runs-on: ubuntu-latest-arm64 # Free for public repos
steps:
- uses: actions/checkout@v6
- name: Build on ARM64
run: |
uname -m # Should output: aarch64
./build.sh
Specifications
- 4 vCPU ARM64 processors
- Available for Linux and Windows
- Native ARM execution (no virtualization needed)
- Ideal for multi-architecture builds
Common Validation Issues
- Using ARM64 runners in private repos without Enterprise Cloud
- Assuming all community actions work on ARM64
- Not testing ARM-specific compilation issues
GPU Runners
Availability
- Generally available for Windows and Linux
- Requires Team or Enterprise Cloud plan
Labels
runs-on: gpu-t4-4-core # NVIDIA Tesla T4
Specifications
- GPU: NVIDIA Tesla T4 with 16GB VRAM
- CPU: 4 vCPUs
- RAM: 28GB
- Pricing: $0.07/minute
Example
jobs:
ml-training:
runs-on: gpu-t4-4-core
steps:
- uses: actions/checkout@v6
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install ML dependencies
run: |
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
pip install -r requirements.txt
- name: Train model
run: python train.py --use-gpu
- name: Run inference
run: python inference.py
Use Cases
- ML model training
- GPU-accelerated testing
- CUDA development
- Image/video processing
Common Validation Issues
- Missing CUDA setup
- Incorrect GPU driver versions
- Not utilizing GPU in workloads (CPU fallback)
- Missing GPU-specific dependencies
M2 Pro macOS Runners (Larger Runners)
Availability
- Generally available with M2 Pro powered runners
Labels
runs-on: macos-latest-xlarge # macOS 15, M2 Pro
runs-on: macos-15-xlarge # macOS 15, M2 Pro
runs-on: macos-14-xlarge # macOS 14, M2 Pro
Specifications
- CPU: 5-core (vs 3-core standard)
- GPU: 8-core with hardware acceleration (enabled by default)
- RAM: 14GB
- Storage: 14GB
- Performance: Up to 15% faster than M1 runners
- Pricing: $0.16/minute
Example
jobs:
ios-build:
runs-on: macos-15-xlarge # M2 Pro with GPU acceleration
steps:
- uses: actions/checkout@v6
- name: Setup Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: latest-stable
- name: Build iOS app
run: |
xcodebuild -workspace App.xcworkspace \
-scheme Production \
-configuration Release \
-archivePath build/App.xcarchive \
archive
- name: Run GPU-accelerated tests
run: |
# GPU acceleration automatically available
xcodebuild test -scheme AppTests
Benefits
- GPU hardware acceleration for Metal-based workloads
- Improved build times for iOS/macOS apps
- Better performance for Xcode builds
- Native Apple Silicon performance
Runner Selection Best Practices
Decision Criteria
- Architecture compatibility: ARM64 vs Intel x86_64
- Cost optimization: Standard vs larger runners
- GPU requirements: ML/AI workloads need GPU runners
- Operating system: Latest versions recommended
- Deprecation timelines: Avoid retired runners
- Public vs private repos: ARM64 free only for public repos
Validation Checklist
# Check these in your workflows:
- [ ] Using latest runner versions (macos-15, windows-2025, ubuntu-latest)
- [ ] Not using deprecated runners (macos-13)
- [ ] Architecture-appropriate runners (ARM64 vs Intel)
- [ ] GPU runners for ML workloads
- [ ] Cost-effective runner selection
- [ ] ARM64 compatibility tested (if using ARM64 runners)
Cost Comparison
| Runner Type | Pricing | Best For |
|---|---|---|
| Standard (Linux/Windows) | Included | Most workloads |
| Standard (macOS) | Included | iOS/macOS builds |
| ARM64 (public repos) | Free | Multi-arch builds |
| ARM64 (private repos) | Enterprise | ARM-native builds |
| GPU (T4) | $0.07/min | ML/AI workloads |
| M2 Pro (xlarge) | $0.16/min | Heavy iOS builds |
Multi-Architecture Builds
Example: Building for Multiple Architectures
jobs:
build:
strategy:
matrix:
include:
- runner: ubuntu-latest
arch: x64
- runner: ubuntu-latest-arm64
arch: arm64
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v6
- name: Build
run: |
echo "Building for ${{ matrix.arch }}"
./build.sh --arch ${{ matrix.arch }}
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: build-${{ matrix.arch }}
path: dist/
Self-Hosted Runner Configuration
When using self-hosted runners, configure actionlint to recognize custom labels:
# .github/actionlint.yaml
self-hosted-runner:
labels:
- my-custom-runner
- gpu-runner
- arm-runner
- on-premises
This prevents actionlint from reporting unknown runner label errors.