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. Trigger Phrases Use this skill when the request includes phrases like: - "validate this GitHub Actions workflow" - "check my file" - "debug actionlint errors" - "test this workflow locally with act" - "verify GitHub Action versions or deprecation…

\"$file\"; then\n log_warn \"$file uses permissions: write-all. Prefer least-privilege scopes.\"\n ((warning_count++))\n fi\n\n # 3) Heuristic detection for untrusted github context in run scripts.\n while IFS= read -r risky_line; do\n log_warn \"$file:$risky_line potential script injection risk in run step. Move untrusted input to env and quote it.\"\n ((warning_count++))\n done \u003c \u003c(\n awk '\n function indent_of(s, t) { t=s; sub(/^[ ]*/, \"\", t); return length(s)-length(t) }\n BEGIN { in_run_block=0; run_indent=-1 }\n {\n line=$0\n indent=indent_of(line)\n if (in_run_block && indent \u003c= run_indent && line !~ /^[[:space:]]*$/) {\n in_run_block=0\n }\n if (line ~ /^[[:space:]]*run:[[:space:]]*[|>][[:space:]]*$/) {\n in_run_block=1\n run_indent=indent\n next\n }\n if (line ~ /^[[:space:]]*run:[[:space:]]*.*\\$\\{\\{[[:space:]]*github\\.(event|head_ref|ref_name|actor|triggering_actor|repository_owner|base_ref)/) {\n print NR\n next\n }\n if (in_run_block && line ~ /\\$\\{\\{[[:space:]]*github\\.(event|head_ref|ref_name|actor|triggering_actor|repository_owner|base_ref)/) {\n print NR\n }\n }\n ' \"$file\"\n )\n\n # 4) OIDC-integrated actions should explicitly request id-token: write.\n if grep -qiE 'uses:[[:space:]]*(aws-actions/configure-aws-credentials|azure/login|google-github-actions/auth|hashicorp/vault-action|actions/attest-build-provenance)@' \"$file\"; then\n if ! grep -qiE 'id-token:[[:space:]]*write' \"$file\"; then\n log_warn \"$file uses an OIDC-related action but does not declare id-token: write in permissions.\"\n ((warning_count++))\n fi\n fi\n done\n\n echo \"\"\n if [ $warning_count -eq 0 ]; then\n log_info \"✓ No security policy warnings found\"\n else\n log_warn \"Security policy warnings: $warning_count (advisory only; exit code unchanged)\"\n log_info \"See references/common_errors.md (Security section) and references/modern_features.md\"\n fi\n\n return 0\n}\n\n# Show reference file hints based on error type\nshow_reference_hints() {\n local error_output=$1\n echo \"\"\n log_section \"Reference Documentation\"\n\n local showed_hint=0\n\n # Check for various error patterns and suggest references\n if echo \"$error_output\" | grep -qi \"syntax\\|yaml\\|unexpected\"; then\n log_reference \"Syntax errors detected - see references/common_errors.md (Syntax Errors section)\"\n showed_hint=1\n fi\n\n if echo \"$error_output\" | grep -qi \"expression\\|\\${{\"; then\n log_reference \"Expression errors detected - see references/common_errors.md (Expression Errors section)\"\n showed_hint=1\n fi\n\n if echo \"$error_output\" | grep -qi \"cron\\|schedule\"; then\n log_reference \"Schedule errors detected - see references/common_errors.md (Schedule Errors section)\"\n showed_hint=1\n fi\n\n if echo \"$error_output\" | grep -qi \"runner\\|runs-on\\|ubuntu\\|macos\\|windows\"; then\n log_reference \"Runner label issues - see references/runners.md\"\n showed_hint=1\n fi\n\n if echo \"$error_output\" | grep -qi \"action\\|uses:\"; then\n log_reference \"Action issues detected - see references/common_errors.md (Action Errors section)\"\n showed_hint=1\n fi\n\n if echo \"$error_output\" | grep -qi \"docker\\|container\"; then\n log_reference \"Docker/container issues - see references/act_usage.md (Troubleshooting section)\"\n showed_hint=1\n fi\n\n if echo \"$error_output\" | grep -qi \"needs:\\|dependency\\|job\"; then\n log_reference \"Job dependency issues - see references/common_errors.md (Job Configuration Errors section)\"\n showed_hint=1\n fi\n\n if echo \"$error_output\" | grep -qi \"injection\\|security\\|secret\\|untrusted\"; then\n log_reference \"Security issues detected - see references/common_errors.md (Security section)\"\n showed_hint=1\n fi\n\n if echo \"$error_output\" | grep -qi \"workflow_call\\|reusable\\|oidc\\|id-token\\|attestation\\|environment:\\|permissions:\"; then\n log_reference \"Modern features - see references/modern_features.md\"\n showed_hint=1\n fi\n\n if echo \"$error_output\" | grep -qi \"version\\|deprecated\\|outdated\\|v[0-9]\"; then\n log_reference \"Action versions - see references/action_versions.md\"\n showed_hint=1\n fi\n\n if [ $showed_hint -eq 0 ]; then\n log_reference \"No direct mapping found for this error output\"\n log_reference \"Fallback: check references/common_errors.md, then search the exact error text in official docs\"\n log_reference \"Include exact tool output, workflow file, and line number in your report\"\n fi\n}\n\n# Validate required tools for selected execution mode\ncheck_tools() {\n local run_actionlint=$1\n local run_act=$2\n local allow_fallback=$3\n local missing_tools=0\n\n if [ \"$run_actionlint\" = true ] && ! tool_exists \"actionlint\"; then\n if [ \"$allow_fallback\" = true ] && [ \"$run_act\" = true ] && tool_exists \"act\"; then\n log_warn \"actionlint not found. Falling back to act-only validation.\"\n run_actionlint=false\n else\n log_error \"actionlint not found. Please run install_tools.sh first.\"\n missing_tools=1\n fi\n fi\n\n if [ \"$run_act\" = true ] && ! tool_exists \"act\"; then\n if [ \"$allow_fallback\" = true ] && [ \"$run_actionlint\" = true ] && tool_exists \"actionlint\"; then\n log_warn \"act not found. Falling back to actionlint-only validation.\"\n run_act=false\n else\n log_error \"act not found. Please run install_tools.sh first.\"\n missing_tools=1\n fi\n fi\n\n if [ $missing_tools -eq 1 ]; then\n log_info \"Run: bash ${SCRIPT_DIR}/install_tools.sh\"\n exit 1\n fi\n\n CHECK_TOOLS_RUN_ACTIONLINT=$run_actionlint\n CHECK_TOOLS_RUN_ACT=$run_act\n}\n\n# Get the appropriate tool path\nget_tool_path() {\n local tool_name=$1\n\n if [ -f \"${TOOLS_DIR}/${tool_name}\" ]; then\n echo \"${TOOLS_DIR}/${tool_name}\"\n elif command -v \"${tool_name}\" &> /dev/null; then\n command -v \"${tool_name}\"\n else\n log_error \"${tool_name} not found\"\n exit 1\n fi\n}\n\n# Validate workflow with actionlint.\n# Captures output to global ACTIONLINT_OUTPUT (for reference hints) while printing it.\nvalidate_with_actionlint() {\n local workflow_path=$1\n log_section \"Running actionlint\"\n\n local actionlint_path\n actionlint_path=$(get_tool_path \"actionlint\")\n\n local output=\"\"\n local actionlint_exit=0\n\n if [ -f \"$workflow_path\" ]; then\n log_info \"Validating: $workflow_path\"\n output=$(\"${actionlint_path}\" \"$workflow_path\" 2>&1) || actionlint_exit=$?\n [ -n \"$output\" ] && echo \"$output\"\n ACTIONLINT_OUTPUT=\"$output\"\n if [ $actionlint_exit -eq 0 ]; then\n log_info \"✓ actionlint validation passed\"\n return 0\n else\n log_error \"✗ actionlint found issues\"\n return 1\n fi\n elif [ -d \"$workflow_path\" ]; then\n log_info \"Validating all workflows in: $workflow_path\"\n\n # Find all .yml and .yaml files\n local workflow_files=()\n while IFS= read -r -d '' file; do\n workflow_files+=(\"$file\")\n done \u003c \u003c(find \"$workflow_path\" -maxdepth 1 -type f \\( -name \"*.yml\" -o -name \"*.yaml\" \\) -print0 2>/dev/null)\n\n if [ ${#workflow_files[@]} -eq 0 ]; then\n log_warn \"No workflow files found in: $workflow_path\"\n return 0\n fi\n\n output=$(\"${actionlint_path}\" \"${workflow_files[@]}\" 2>&1) || actionlint_exit=$?\n [ -n \"$output\" ] && echo \"$output\"\n ACTIONLINT_OUTPUT=\"$output\"\n if [ $actionlint_exit -eq 0 ]; then\n log_info \"✓ actionlint validation passed for ${#workflow_files[@]} file(s)\"\n return 0\n else\n log_error \"✗ actionlint found issues\"\n return 1\n fi\n else\n log_error \"Path not found: $workflow_path\"\n return 1\n fi\n}\n\n# Test workflow with act\ntest_with_act() {\n local workflow_path=$1\n log_section \"Running act (validation)\"\n ACT_SKIP_REASON=\"\"\n\n # Check if Docker is running\n if ! check_docker; then\n log_error \"Docker is not running!\"\n log_warn \"act requires Docker to validate and test workflows.\"\n log_warn \"\"\n log_warn \"Solutions:\"\n log_warn \" 1. Start Docker Desktop or Docker daemon\"\n log_warn \" 2. Use --lint-only flag to skip act testing\"\n log_warn \"\"\n return 1\n fi\n\n local act_path=$(get_tool_path \"act\")\n\n # Convert workflow_path to absolute path\n local abs_workflow_path=\"$workflow_path\"\n if [[ ! \"$abs_workflow_path\" = /* ]]; then\n abs_workflow_path=\"$(cd \"$(dirname \"$workflow_path\")\" 2>/dev/null && pwd)/$(basename \"$workflow_path\")\" || abs_workflow_path=\"$workflow_path\"\n fi\n\n # Find the repository root (where .github/workflows exists)\n local repo_root=\"\"\n local search_path=\"$abs_workflow_path\"\n\n # If workflow_path is a file, get its directory for searching\n if [ -f \"$search_path\" ]; then\n search_path=\"$(dirname \"$search_path\")\"\n fi\n\n # Search upwards for .github/workflows directory\n local current_dir=\"$search_path\"\n while [ \"$current_dir\" != \"/\" ]; do\n if [ -d \"$current_dir/.github/workflows\" ]; then\n repo_root=\"$current_dir\"\n break\n fi\n # Also check if we're inside .github/workflows\n if [[ \"$current_dir\" == *\"/.github/workflows\"* ]] || [[ \"$current_dir\" == *\"/.github/workflows\" ]]; then\n # Extract the part before .github\n repo_root=\"${current_dir%%/.github/workflows*}\"\n if [ -d \"$repo_root/.github/workflows\" ]; then\n break\n fi\n fi\n current_dir=\"$(dirname \"$current_dir\")\"\n done\n\n # Fallback to current directory\n if [ -z \"$repo_root\" ] && [ -d \"./.github/workflows\" ]; then\n repo_root=\"$(pwd)\"\n fi\n\n if [ -z \"$repo_root\" ]; then\n log_warn \"No .github/workflows directory found in path hierarchy\"\n log_warn \"Skipping act validation - workflows must be in .github/workflows/ directory\"\n log_info \"Searched from: $workflow_path\"\n ACT_SKIP_REASON=\"no .github/workflows directory found in path hierarchy\"\n return 2\n fi\n\n log_info \"Repository root: $repo_root\"\n\n # Determine the workflow file(s) to validate with act\n # act requires workflows to be in .github/workflows/ directory\n local workflow_flag=\"\"\n local target_description=\"\"\n\n if [ -f \"$abs_workflow_path\" ]; then\n # Check if the file is inside .github/workflows\n if [[ \"$abs_workflow_path\" == *\"/.github/workflows/\"* ]]; then\n # File is in .github/workflows - use -W flag with relative path\n workflow_flag=\"-W ${abs_workflow_path#$repo_root/}\"\n target_description=\"workflow: $(basename \"$abs_workflow_path\")\"\n else\n # File is outside .github/workflows (e.g., examples/)\n # act cannot directly validate files outside .github/workflows\n log_warn \"Target file is outside .github/workflows/: $abs_workflow_path\"\n log_warn \"act can only validate workflows in .github/workflows/ directory\"\n log_info \"Skipping act validation for this file\"\n log_info \"Note: actionlint validation still applies to this file\"\n ACT_SKIP_REASON=\"target file is outside .github/workflows\"\n return 2\n fi\n elif [ -d \"$abs_workflow_path\" ]; then\n # Directory specified\n if [[ \"$abs_workflow_path\" == *\"/.github/workflows\"* ]] || [[ \"$abs_workflow_path\" == \"$repo_root/.github/workflows\" ]]; then\n # It's the .github/workflows directory - validate all workflows\n workflow_flag=\"\"\n target_description=\"all workflows in .github/workflows/\"\n else\n # Directory outside .github/workflows\n log_warn \"Target directory is outside .github/workflows/: $abs_workflow_path\"\n log_warn \"act can only validate workflows in .github/workflows/ directory\"\n log_info \"Skipping act validation for this directory\"\n ACT_SKIP_REASON=\"target directory is outside .github/workflows\"\n return 2\n fi\n fi\n\n # Save current directory\n local original_dir=\"$(pwd)\"\n\n # Change to repository root for act\n cd \"$repo_root\" || {\n log_error \"Failed to change to repository root: $repo_root\"\n return 1\n }\n\n log_info \"Target: $target_description\"\n log_info \"Step 1: Listing workflows...\"\n echo \"\"\n\n # Define default runner images to avoid interactive prompts\n # Using medium-sized images for good compatibility without huge downloads\n local runner_images=(\n \"-P\" \"ubuntu-latest=catthehacker/ubuntu:act-latest\"\n \"-P\" \"ubuntu-22.04=catthehacker/ubuntu:act-22.04\"\n \"-P\" \"ubuntu-20.04=catthehacker/ubuntu:act-20.04\"\n )\n\n # Use --list to show available workflows\n # This validates that workflows can be parsed\n local list_cmd=\"${act_path} --list ${workflow_flag} ${runner_images[*]}\"\n log_info \"Running: act --list ${workflow_flag}\"\n if ! eval \"${list_cmd}\" 2>&1 | head -30; then\n log_warn \"Could not list workflows - this may indicate parsing issues\"\n echo \"\"\n else\n echo \"\"\n log_info \"✓ Workflow listing successful\"\n fi\n\n echo \"\"\n log_info \"Step 2: Validating workflow syntax with dry-run...\"\n log_info \"Note: This validates workflow structure without executing jobs\"\n log_info \"Using medium-sized runner images (catthehacker/ubuntu:act-*)\"\n echo \"\"\n\n # Run act in dry-run mode\n # --dryrun: validates without executing\n # --container-architecture: ensures consistent platform\n # -W: specifies the workflow file to validate\n # -P: specifies runner images to avoid interactive prompt\n # 2>&1: capture both stdout and stderr\n local act_output\n local act_exit_code\n\n local dryrun_cmd=\"${act_path} --dryrun ${workflow_flag} --container-architecture linux/amd64 ${runner_images[*]}\"\n log_info \"Running: act --dryrun ${workflow_flag} --container-architecture linux/amd64\"\n act_output=$(eval \"${dryrun_cmd}\" 2>&1)\n act_exit_code=$?\n\n # Display output\n echo \"$act_output\"\n echo \"\"\n\n # Interpret results\n if [ $act_exit_code -eq 0 ]; then\n log_info \"✓ act validation passed\"\n cd \"$original_dir\"\n return 0\n else\n # Check for specific error conditions\n if echo \"$act_output\" | grep -qi \"EOF\"; then\n log_error \"✗ act encountered EOF error\"\n log_warn \"This should not happen with -P flags set\"\n log_info \"Try running: act --list manually to diagnose\"\n cd \"$original_dir\"\n return 1\n elif echo \"$act_output\" | grep -q \"unable to get git repo\"; then\n log_warn \"Not a git repository - some act features limited\"\n log_info \"act validation completed with warnings\"\n cd \"$original_dir\"\n return 0\n elif echo \"$act_output\" | grep -qi \"pull access denied\\|image.*not found\"; then\n log_error \"✗ Docker image pull failed\"\n log_warn \"Cannot pull runner images. This may be due to:\"\n log_warn \" - Docker registry connectivity issues\"\n log_warn \" - Rate limiting\"\n log_warn \"First-time run will download ~500MB of images\"\n cd \"$original_dir\"\n return 1\n elif echo \"$act_output\" | grep -qi \"error\\|failed\"; then\n log_error \"✗ act validation failed (exit code: $act_exit_code)\"\n log_warn \"This may indicate:\"\n log_warn \" - Workflow syntax errors\"\n log_warn \" - Invalid action references\"\n log_warn \" - Docker image issues\"\n log_warn \" - Configuration problems\"\n cd \"$original_dir\"\n return 1\n else\n log_warn \"act completed with warnings (exit code: $act_exit_code)\"\n cd \"$original_dir\"\n return 0\n fi\n fi\n}\n\n# Display usage\n# Optional first argument: exit code (default 0, pass 1 for error paths)\nusage() {\n local exit_code=\"${1:-0}\"\n echo \"Usage: $0 [OPTIONS] \u003cworkflow-file-or-directory>\"\n echo \"\"\n echo \"Options:\"\n echo \" --lint-only Run only actionlint validation\"\n echo \" --test-only Run only act testing (requires Docker)\"\n echo \" --check-versions Check action versions against recommended versions\"\n echo \" --policy-checks Run advisory security policy checks (warnings only)\"\n echo \" --help Display this help message\"\n echo \"\"\n echo \"Examples:\"\n echo \" $0 .github/workflows/ci.yml\"\n echo \" $0 .github/workflows/\"\n echo \" $0 --lint-only .github/workflows/ci.yml\"\n echo \" $0 --test-only .github/workflows/\"\n echo \" $0 --check-versions .github/workflows/ci.yml\"\n echo \" $0 --lint-only --policy-checks .github/workflows/ci.yml\"\n echo \"\"\n echo \"Requirements:\"\n echo \" - actionlint: For static analysis (installed via install_tools.sh)\"\n echo \" - act: For workflow testing (installed via install_tools.sh)\"\n echo \" - Docker: Required for act to run (must be running)\"\n echo \"\"\n exit \"$exit_code\"\n}\n\n# Main validation\nmain() {\n local workflow_path=\"\"\n local lint_only=false\n local test_only=false\n local check_versions=false\n local policy_checks=false\n local version_only=false\n local run_actionlint=true\n local run_act=true\n local allow_tool_fallback=false\n local docker_available=true\n local did_actionlint=false\n local did_act=false\n local act_skip_reason=\"\"\n local act_result=0\n CHECK_TOOLS_RUN_ACTIONLINT=true\n CHECK_TOOLS_RUN_ACT=true\n ACT_SKIP_REASON=\"\"\n\n # Parse arguments\n while [[ $# -gt 0 ]]; do\n case $1 in\n --lint-only)\n lint_only=true\n shift\n ;;\n --test-only)\n test_only=true\n shift\n ;;\n --check-versions)\n check_versions=true\n shift\n ;;\n --policy-checks)\n policy_checks=true\n shift\n ;;\n --help)\n usage\n ;;\n *)\n workflow_path=$1\n shift\n ;;\n esac\n done\n\n if [ -z \"$workflow_path\" ]; then\n log_error \"No workflow file or directory specified\"\n echo \"\"\n usage 1\n fi\n\n if [ \"$lint_only\" = true ] && [ \"$test_only\" = true ]; then\n log_error \"Cannot combine --lint-only and --test-only\"\n exit 1\n fi\n\n # Determine mode-specific execution\n if [ \"$test_only\" = true ]; then\n run_actionlint=false\n run_act=true\n elif [ \"$lint_only\" = true ]; then\n run_actionlint=true\n run_act=false\n else\n run_actionlint=true\n run_act=true\n allow_tool_fallback=true\n fi\n\n if [ \"$check_versions\" = true ] && [ \"$lint_only\" = false ] && [ \"$test_only\" = false ] && [ \"$policy_checks\" = false ]; then\n version_only=true\n run_actionlint=false\n run_act=false\n allow_tool_fallback=false\n fi\n\n log_section \"GitHub Actions Validator\"\n log_info \"Target: $workflow_path\"\n\n check_tools \"$run_actionlint\" \"$run_act\" \"$allow_tool_fallback\"\n run_actionlint=$CHECK_TOOLS_RUN_ACTIONLINT\n run_act=$CHECK_TOOLS_RUN_ACT\n\n # Pre-check Docker status if act testing is enabled\n if [ \"$run_act\" = true ]; then\n if ! precheck_docker; then\n if [ \"$test_only\" = true ]; then\n log_error \"Docker is required for --test-only mode\"\n exit 1\n fi\n if [ \"$run_actionlint\" = true ]; then\n docker_available=false\n run_act=false\n log_warn \"Proceeding without act because Docker is unavailable\"\n else\n log_error \"Docker is required for act validation in the selected mode\"\n exit 1\n fi\n fi\n fi\n\n local exit_code=0\n ACTIONLINT_OUTPUT=\"\"\n\n # Run version check if requested\n if [ \"$check_versions\" = true ]; then\n if ! check_action_versions \"$workflow_path\"; then\n exit_code=1\n fi\n # If only checking versions, exit here\n if [ \"$version_only\" = true ]; then\n log_section \"Version Check Complete\"\n exit $exit_code\n fi\n fi\n\n # Run actionlint (output is captured and printed inside validate_with_actionlint,\n # and stored in ACTIONLINT_OUTPUT for reference hint routing)\n if [ \"$run_actionlint\" = true ]; then\n did_actionlint=true\n if ! validate_with_actionlint \"$workflow_path\"; then\n exit_code=1\n fi\n fi\n\n # Run advisory security checks if requested.\n if [ \"$policy_checks\" = true ]; then\n if ! check_security_policies \"$workflow_path\"; then\n exit_code=1\n fi\n fi\n\n # Run act if enabled and Docker is available\n if [ \"$run_act\" = true ] && [ \"$docker_available\" = true ]; then\n test_with_act \"$workflow_path\"\n act_result=$?\n if [ $act_result -eq 0 ]; then\n did_act=true\n elif [ $act_result -eq 2 ]; then\n act_skip_reason=\"$ACT_SKIP_REASON\"\n log_warn \"act validation skipped: $act_skip_reason\"\n if [ \"$did_actionlint\" = false ]; then\n log_error \"No effective validator executed: act was skipped and actionlint did not run\"\n exit_code=1\n fi\n else\n exit_code=1\n fi\n fi\n\n if [ \"$version_only\" = false ] && [ \"$did_actionlint\" = false ] && [ \"$did_act\" = false ]; then\n log_error \"No validator executed; refusing to report success\"\n exit_code=1\n fi\n\n log_section \"Validation Summary\"\n if [ $exit_code -eq 0 ]; then\n if [ -n \"$act_skip_reason\" ]; then\n log_info \"✓ Validation passed (actionlint completed; act skipped: $act_skip_reason)\"\n else\n log_info \"✓ All validations passed\"\n fi\n else\n log_error \"✗ Some validations failed\"\n\n # Show reference hints based on errors\n if [ -n \"$ACTIONLINT_OUTPUT\" ]; then\n show_reference_hints \"$ACTIONLINT_OUTPUT\"\n fi\n\n echo \"\"\n log_info \"Tips:\"\n log_info \" - Review error messages above\"\n log_info \" - Use --lint-only to skip Docker-dependent tests\"\n log_info \" - Use --check-versions to check for outdated actions\"\n log_info \" - Use --policy-checks for security hardening warnings\"\n log_info \" - Check references/common_errors.md for solutions\"\n fi\n\n exit $exit_code\n}\n\nmain \"$@\"\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":33091,"content_sha256":"d1078eb9946e569376550111ba97473ea64ae70b5bea3c58d0400c01bb582e02"},{"filename":"tests/test_validate_workflow.sh","content":"#!/usr/bin/env bash\n#\n# Regression test suite for scripts/validate_workflow.sh\n#\n# Covers:\n# - P0: no false-success when actionlint is missing and act cannot validate target\n# - P1: advisory policy checks (SHA pinning, permissions, script injection, OIDC)\n#\n\nset -euo pipefail\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\nreadonly SCRIPT_DIR\nSKILL_DIR=\"$(cd \"$SCRIPT_DIR/..\" && pwd)\"\nreadonly SKILL_DIR\nVALIDATOR_SOURCE=\"$SKILL_DIR/scripts/validate_workflow.sh\"\nreadonly VALIDATOR_SOURCE\n\nTMP_ROOT=\"$(mktemp -d)\"\ncleanup() {\n rm -rf \"$TMP_ROOT\"\n}\ntrap cleanup EXIT\n\nPASS=0\nFAIL=0\nOUTPUT=\"\"\nEXIT_CODE=0\n\npass() { echo \" PASS: $1\"; PASS=$((PASS + 1)); }\nfail() { echo \" FAIL: $1\"; FAIL=$((FAIL + 1)); }\n\nnew_sandbox() {\n SANDBOX=\"$(mktemp -d \"$TMP_ROOT/case-XXXXXX\")\"\n\n mkdir -p \"$SANDBOX/skill/scripts/.tools\"\n mkdir -p \"$SANDBOX/repo/.github/workflows\"\n mkdir -p \"$SANDBOX/repo/examples\"\n mkdir -p \"$SANDBOX/bin\"\n\n cp \"$VALIDATOR_SOURCE\" \"$SANDBOX/skill/scripts/validate_workflow.sh\"\n chmod +x \"$SANDBOX/skill/scripts/validate_workflow.sh\"\n\n cat > \"$SANDBOX/bin/docker\" \u003c\u003c'EOF'\n#!/usr/bin/env bash\nif [[ \"${1:-}\" == \"info\" ]]; then\n exit \"${DOCKER_INFO_STUB_EXIT:-0}\"\nfi\nexit 0\nEOF\n chmod +x \"$SANDBOX/bin/docker\"\n}\n\ncreate_act_stub() {\n cat > \"$SANDBOX/skill/scripts/.tools/act\" \u003c\u003c'EOF'\n#!/usr/bin/env bash\nif [[ \"$*\" == *\"--list\"* ]]; then\n exit \"${ACT_LIST_STUB_EXIT:-0}\"\nfi\nif [[ \"$*\" == *\"--dryrun\"* ]]; then\n exit \"${ACT_DRYRUN_STUB_EXIT:-0}\"\nfi\nexit \"${ACT_STUB_EXIT:-0}\"\nEOF\n chmod +x \"$SANDBOX/skill/scripts/.tools/act\"\n}\n\ncreate_actionlint_stub() {\n cat > \"$SANDBOX/skill/scripts/.tools/actionlint\" \u003c\u003c'EOF'\n#!/usr/bin/env bash\nexit \"${ACTIONLINT_STUB_EXIT:-0}\"\nEOF\n chmod +x \"$SANDBOX/skill/scripts/.tools/actionlint\"\n}\n\nrun_validator() {\n local -a args=(\"$@\")\n OUTPUT=\"\"\n EXIT_CODE=0\n OUTPUT=$(\n cd \"$SANDBOX/repo\" && \\\n PATH=\"$SANDBOX/bin:$PATH\" bash \"$SANDBOX/skill/scripts/validate_workflow.sh\" \"${args[@]}\" 2>&1\n ) || EXIT_CODE=$?\n}\n\nassert_exit() {\n local label=\"$1\"\n local expected=\"$2\"\n if [[ \"$EXIT_CODE\" -eq \"$expected\" ]]; then\n pass \"$label (exit $EXIT_CODE)\"\n else\n fail \"$label (expected exit $expected, got $EXIT_CODE)\"\n echo \"$OUTPUT\" | sed 's/^/ /'\n fi\n}\n\nassert_contains() {\n local label=\"$1\"\n local pattern=\"$2\"\n if echo \"$OUTPUT\" | grep -qE \"$pattern\"; then\n pass \"$label\"\n else\n fail \"$label (pattern not found: $pattern)\"\n echo \"$OUTPUT\" | sed 's/^/ /'\n fi\n}\n\nassert_not_contains() {\n local label=\"$1\"\n local pattern=\"$2\"\n if echo \"$OUTPUT\" | grep -qE \"$pattern\"; then\n fail \"$label (unexpected pattern found: $pattern)\"\n echo \"$OUTPUT\" | sed 's/^/ /'\n else\n pass \"$label\"\n fi\n}\n\necho \"Running github-actions-validator regression tests...\"\necho \"\"\n\necho \"[P0] actionlint missing + target outside .github/workflows must fail\"\nnew_sandbox\ncreate_act_stub\ncat > \"$SANDBOX/repo/examples/outside.yml\" \u003c\u003c'EOF'\nname: Outside\non: push\njobs:\n build:\n runs-on: ubuntu-latest\n steps:\n - run: echo hi\nEOF\nrun_validator \"$SANDBOX/repo/examples/outside.yml\"\nif [[ \"$EXIT_CODE\" -ne 0 ]]; then\n pass \"returns non-zero when no effective validator executed\"\nelse\n fail \"returns non-zero when no effective validator executed (expected non-zero, got 0)\"\n echo \"$OUTPUT\" | sed 's/^/ /'\nfi\nassert_contains \"reports skipped act path\" \"act validation skipped\"\nassert_contains \"reports no effective validator\" \"No effective validator executed|No validator executed; refusing to report success\"\n\necho \"\"\necho \"[P0] actionlint run + act skip should still pass\"\nnew_sandbox\ncreate_act_stub\ncreate_actionlint_stub\ncat > \"$SANDBOX/repo/examples/outside.yml\" \u003c\u003c'EOF'\nname: Outside\non: push\njobs:\n build:\n runs-on: ubuntu-latest\n steps:\n - run: echo hi\nEOF\nrun_validator \"$SANDBOX/repo/examples/outside.yml\"\nassert_exit \"passes when actionlint runs and act skips unsupported target\" 0\nassert_contains \"shows actionlint success\" \"actionlint validation passed\"\nassert_contains \"shows act skip message\" \"act validation skipped: target file is outside \\\\.github/workflows\"\n\necho \"\"\necho \"[P0] fallback to act-only still works for real workflow paths\"\nnew_sandbox\ncreate_act_stub\ncat > \"$SANDBOX/repo/.github/workflows/ci.yml\" \u003c\u003c'EOF'\nname: CI\non: push\njobs:\n build:\n runs-on: ubuntu-latest\n steps:\n - run: echo hi\nEOF\nrun_validator \"$SANDBOX/repo/.github/workflows/ci.yml\"\nassert_exit \"passes in act-only fallback mode for workflow under .github/workflows\" 0\nassert_contains \"warns about actionlint fallback\" \"actionlint not found\\\\. Falling back to act-only validation\"\nassert_contains \"act dry-run success is reported\" \"act validation passed\"\n\necho \"\"\necho \"[P1] policy checks report hardening warnings (advisory)\"\nnew_sandbox\ncreate_actionlint_stub\ncat > \"$SANDBOX/repo/examples/policy-bad.yml\" \u003c\u003c'EOF'\nname: Policy Bad\non: pull_request\njobs:\n release:\n runs-on: ubuntu-latest\n steps:\n - uses: docker/build-push-action@v6\n - uses: aws-actions/configure-aws-credentials@v4\n - name: Unsafe run usage\n run: echo \"${{ github.event.pull_request.title }}\"\nEOF\nrun_validator --lint-only --policy-checks \"$SANDBOX/repo/examples/policy-bad.yml\"\nassert_exit \"policy warnings do not change exit code\" 0\nassert_contains \"warns for unpinned third-party action\" \"third-party action is not SHA pinned: docker/build-push-action@v6\"\nassert_contains \"warns for missing permissions\" \"missing explicit permissions block\"\nassert_contains \"warns for script injection pattern\" \"potential script injection risk in run step\"\nassert_contains \"warns for missing id-token with OIDC action\" \"OIDC-related action but does not declare id-token: write\"\nassert_contains \"prints policy warning summary\" \"Security policy warnings:\"\n\necho \"\"\necho \"[P1] policy checks accept hardened workflow\"\nnew_sandbox\ncreate_actionlint_stub\ncat > \"$SANDBOX/repo/examples/policy-good.yml\" \u003c\u003c'EOF'\nname: Policy Good\non: pull_request\npermissions:\n contents: read\n id-token: write\njobs:\n release:\n runs-on: ubuntu-latest\n steps:\n - uses: docker/build-push-action@0123456789abcdef0123456789abcdef01234567\n - uses: aws-actions/configure-aws-credentials@0123456789abcdef0123456789abcdef01234567\n - name: Safe run usage\n env:\n PR_TITLE: ${{ github.event.pull_request.title }}\n run: echo \"$PR_TITLE\"\nEOF\nrun_validator --lint-only --policy-checks \"$SANDBOX/repo/examples/policy-good.yml\"\nassert_exit \"hardened workflow remains successful\" 0\nassert_contains \"prints clean policy summary\" \"No security policy warnings found\"\nassert_not_contains \"does not print warning summary when clean\" \"Security policy warnings:\"\n\necho \"\"\necho \"Test summary: PASS=$PASS FAIL=$FAIL\"\nif [[ \"$FAIL\" -ne 0 ]]; then\n exit 1\nfi\n\necho \"All tests passed.\"\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":6914,"content_sha256":"c1622b418f22e378543a02b1b9e5bbc55343c3f4a17a7a440f0b811c6c12fa03"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"GitHub Actions Validator","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Overview","type":"text"}]},{"type":"paragraph","content":[{"text":"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.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Trigger Phrases","type":"text"}]},{"type":"paragraph","content":[{"text":"Use this skill when the request includes phrases like:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"validate this GitHub Actions workflow\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"check my ","type":"text"},{"text":".github/workflows/*.yml","type":"text","marks":[{"type":"code_inline"}]},{"text":" file\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"debug actionlint errors\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"test this workflow locally with act\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"verify GitHub Action versions or deprecations\"","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"When to Use This Skill","type":"text"}]},{"type":"paragraph","content":[{"text":"Use this skill when:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Validating workflow files","type":"text","marks":[{"type":"strong"}]},{"text":": Checking ","type":"text"},{"text":".github/workflows/*.yml","type":"text","marks":[{"type":"code_inline"}]},{"text":" for syntax errors and best practices","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Testing workflows locally","type":"text","marks":[{"type":"strong"}]},{"text":": Running workflows with ","type":"text"},{"text":"act","type":"text","marks":[{"type":"code_inline"}]},{"text":" before pushing to GitHub","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Debugging workflow failures","type":"text","marks":[{"type":"strong"}]},{"text":": Identifying issues in workflow configuration","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Validating custom actions","type":"text","marks":[{"type":"strong"}]},{"text":": Checking composite, Docker, or JavaScript actions","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Verifying public actions","type":"text","marks":[{"type":"strong"}]},{"text":": Validating usage of actions from GitHub Marketplace","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Pre-commit validation","type":"text","marks":[{"type":"strong"}]},{"text":": Ensuring workflows are valid before committing","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Required Execution Flow","type":"text"}]},{"type":"paragraph","content":[{"text":"Every validation run should follow these steps in order.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 1: Set Skill Path and Run Validation","type":"text"}]},{"type":"paragraph","content":[{"text":"Run commands from the repository root that contains ","type":"text"},{"text":".github/workflows/","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"SKILL_DIR=\"devops-skills-plugin/skills/github-actions-validator\"\nbash \"$SKILL_DIR/scripts/validate_workflow.sh\" \u003cworkflow-file-or-directory>","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 2: Map Each Error to a Reference","type":"text"}]},{"type":"paragraph","content":[{"text":"For each actionlint/act error, consult the mapping table below, then extract the matching fix pattern.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 3: Apply Minimal-Quote Policy","type":"text"}]},{"type":"paragraph","content":[{"text":"For each issue:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Include the exact error line from tool output.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Quote only the smallest useful snippet from ","type":"text"},{"text":"references/","type":"text","marks":[{"type":"code_inline"}]},{"text":" (prefer \u003c=8 lines).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Paraphrase the rest and cite the source file/section.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Show corrected workflow code.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 4: Handle Unmapped Errors Explicitly","type":"text"}]},{"type":"paragraph","content":[{"text":"If an error does not match any mapping:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Label it as ","type":"text"},{"text":"UNMAPPED","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Capture exact tool output, workflow file, and line number (if available).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Check ","type":"text"},{"text":"references/common_errors.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" general sections first.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"If still unresolved, search official docs with the exact error string.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Mark the fix as ","type":"text"},{"text":"provisional","type":"text","marks":[{"type":"code_inline"}]},{"text":" until post-fix rerun passes.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 5: Verify Public Action Versions","type":"text"}]},{"type":"paragraph","content":[{"text":"For each ","type":"text"},{"text":"uses: owner/action@version","type":"text","marks":[{"type":"code_inline"}]},{"text":":","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Check ","type":"text"},{"text":"references/action_versions.md","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"For unknown actions, verify against official docs.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Confirm required inputs and deprecations.","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Offline mode behavior:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"If network/doc lookup is unavailable, rely on ","type":"text"},{"text":"references/action_versions.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" only.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Mark unknown actions as ","type":"text"},{"text":"UNVERIFIED-OFFLINE","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Do not claim \"latest\" version without an online verification pass.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 6: Mandatory Post-Fix Rerun","type":"text"}]},{"type":"paragraph","content":[{"text":"After applying fixes, rerun validation before finalizing:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"SKILL_DIR=\"devops-skills-plugin/skills/github-actions-validator\"\nbash \"$SKILL_DIR/scripts/validate_workflow.sh\" \u003cworkflow-file-or-directory>","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 7: Provide Final Summary","type":"text"}]},{"type":"paragraph","content":[{"text":"Final output should include:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Issues found and fixes applied","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Any ","type":"text"},{"text":"UNMAPPED","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"UNVERIFIED-OFFLINE","type":"text","marks":[{"type":"code_inline"}]},{"text":" items","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Post-fix rerun command and result","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Remaining warnings/risk notes","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Error Type to Reference File Mapping","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Error Pattern in Output","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Reference File to Read","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Section to Quote","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"runs-on:","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"runner","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"ubuntu","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"macos","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"windows","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/runners.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Runner labels","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"cron","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"schedule","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/common_errors.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Schedule Errors","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"${{","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"expression","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"if:","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/common_errors.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Expression Errors","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"needs:","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"job","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"dependency","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/common_errors.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Job Configuration Errors","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"uses:","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"action","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"input","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/common_errors.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Action Errors","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"untrusted","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"injection","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"security","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/common_errors.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Script Injection section","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"syntax","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"yaml","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"unexpected","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/common_errors.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Syntax Errors","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"docker","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"container","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/act_usage.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Troubleshooting","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"@v3","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"@v4","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"deprecated","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"outdated","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/action_versions.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Version table","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"workflow_call","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"reusable","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"oidc","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/modern_features.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Relevant section","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"glob","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"path","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"paths:","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"pattern","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/common_errors.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Path Filter Errors","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Example: Complete Error Handling Workflow","type":"text"}]},{"type":"paragraph","content":[{"text":"User's workflow has this error:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"runs-on: ubuntu-lastest","type":"text"}]},{"type":"paragraph","content":[{"text":"Step 1 - Script output:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"label \"ubuntu-lastest\" is unknown","type":"text"}]},{"type":"paragraph","content":[{"text":"Step 2 - Read ","type":"text","marks":[{"type":"strong"}]},{"text":"references/runners.md","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" or ","type":"text","marks":[{"type":"strong"}]},{"text":"references/common_errors.md","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":":","type":"text","marks":[{"type":"strong"}]},{"text":" Find the \"Invalid Runner Label\" section.","type":"text"}]},{"type":"paragraph","content":[{"text":"Step 3 - Quote the fix to user:","type":"text","marks":[{"type":"strong"}]}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"Error:","type":"text","marks":[{"type":"strong"}]},{"text":" ","type":"text"},{"text":"label \"ubuntu-lastest\" is unknown","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"paragraph","content":[{"text":"Cause:","type":"text","marks":[{"type":"strong"}]},{"text":" Typo in runner label (from ","type":"text"},{"text":"references/common_errors.md","type":"text","marks":[{"type":"code_inline"}]},{"text":"):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"# Bad\nruns-on: ubuntu-lastest # Typo","type":"text"}]},{"type":"paragraph","content":[{"text":"Fix","type":"text","marks":[{"type":"strong"}]},{"text":" (from ","type":"text"},{"text":"references/common_errors.md","type":"text","marks":[{"type":"code_inline"}]},{"text":"):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"# Good\nruns-on: ubuntu-latest","type":"text"}]},{"type":"paragraph","content":[{"text":"Valid runner labels","type":"text","marks":[{"type":"strong"}]},{"text":" (from ","type":"text"},{"text":"references/runners.md","type":"text","marks":[{"type":"code_inline"}]},{"text":"):","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"ubuntu-latest","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"ubuntu-24.04","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"ubuntu-22.04","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"windows-latest","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"windows-2025","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"windows-2022","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"macos-latest","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"macos-15","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"macos-14","type":"text","marks":[{"type":"code_inline"}]}]}]}]}]},{"type":"paragraph","content":[{"text":"Step 4 - Provide corrected code:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"runs-on: ubuntu-latest","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Quick Start","type":"text"}]},{"type":"paragraph","content":[{"text":"Set once per shell session:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"SKILL_DIR=\"devops-skills-plugin/skills/github-actions-validator\"","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Initial Setup","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"bash \"$SKILL_DIR/scripts/install_tools.sh\"","type":"text"}]},{"type":"paragraph","content":[{"text":"This installs ","type":"text"},{"text":"act","type":"text","marks":[{"type":"strong"}]},{"text":" (local workflow execution) and ","type":"text"},{"text":"actionlint","type":"text","marks":[{"type":"strong"}]},{"text":" (static analysis) to ","type":"text"},{"text":"scripts/.tools/","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Basic Validation","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Validate a single workflow\nbash \"$SKILL_DIR/scripts/validate_workflow.sh\" .github/workflows/ci.yml\n\n# Validate all workflows\nbash \"$SKILL_DIR/scripts/validate_workflow.sh\" .github/workflows/\n\n# Lint-only (fastest)\nbash \"$SKILL_DIR/scripts/validate_workflow.sh\" --lint-only .github/workflows/ci.yml\n\n# Test-only with act (requires Docker)\nbash \"$SKILL_DIR/scripts/validate_workflow.sh\" --test-only .github/workflows/","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Core Validation Workflow","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"1. Static Analysis with actionlint","type":"text"}]},{"type":"paragraph","content":[{"text":"Start with static analysis to catch syntax errors and common issues:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"bash \"$SKILL_DIR/scripts/validate_workflow.sh\" --lint-only .github/workflows/ci.yml","type":"text"}]},{"type":"paragraph","content":[{"text":"What actionlint checks:","type":"text","marks":[{"type":"strong"}]},{"text":" YAML syntax, schema compliance, expression syntax, runner labels, action inputs/outputs, job dependencies, CRON syntax, glob patterns, shell scripts, security vulnerabilities.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"2. Local Testing with act","type":"text"}]},{"type":"paragraph","content":[{"text":"After passing static analysis, test workflow execution:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"bash \"$SKILL_DIR/scripts/validate_workflow.sh\" --test-only .github/workflows/","type":"text"}]},{"type":"paragraph","content":[{"text":"Note:","type":"text","marks":[{"type":"strong"}]},{"text":" act has limitations - see ","type":"text"},{"text":"references/act_usage.md","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"3. Full Validation","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"bash \"$SKILL_DIR/scripts/validate_workflow.sh\" .github/workflows/ci.yml","type":"text"}]},{"type":"paragraph","content":[{"text":"Default behavior if tools/runtime are unavailable:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"If ","type":"text"},{"text":"act","type":"text","marks":[{"type":"code_inline"}]},{"text":" is missing, full validation falls back to actionlint-only.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"If Docker is unavailable, full validation skips act and continues with actionlint.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"--check-versions","type":"text","marks":[{"type":"code_inline"}]},{"text":" works in offline/local mode using ","type":"text"},{"text":"references/action_versions.md","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Validating Resource Types","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Workflows","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Single workflow\nbash \"$SKILL_DIR/scripts/validate_workflow.sh\" .github/workflows/ci.yml\n\n# All workflows\nbash \"$SKILL_DIR/scripts/validate_workflow.sh\" .github/workflows/","type":"text"}]},{"type":"paragraph","content":[{"text":"Key validation points:","type":"text","marks":[{"type":"strong"}]},{"text":" triggers, job configurations, runner labels, environment variables, secrets, conditionals, matrix strategies.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Custom Local Actions","type":"text"}]},{"type":"paragraph","content":[{"text":"Create a test workflow that uses the custom action, then validate:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"bash \"$SKILL_DIR/scripts/validate_workflow.sh\" .github/workflows/test-custom-action.yml","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Public Actions","type":"text"}]},{"type":"paragraph","content":[{"text":"When workflows use public actions (e.g., ","type":"text"},{"text":"actions/checkout@v6","type":"text","marks":[{"type":"code_inline"}]},{"text":"):","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Check ","type":"text"},{"text":"references/action_versions.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" first","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use official docs (or web search) for unknown actions","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Verify required inputs and version","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Check for deprecation warnings","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Run validation script","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"If offline:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Mark unknown versions as ","type":"text"},{"text":"UNVERIFIED-OFFLINE","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Avoid \"latest/current\" claims until online verification is possible","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Search format:","type":"text","marks":[{"type":"strong"}]},{"text":" ","type":"text"},{"text":"\"[action-name] [version] github action documentation\"","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Reference File Consultation Guide","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Mandatory Reference Consultation","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Situation","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Reference File","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Action","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"actionlint reports any mapped error","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/common_errors.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Find matching error and apply minimal quote policy","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"actionlint reports unmapped error","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/common_errors.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" + official docs","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Label as ","type":"text"},{"text":"UNMAPPED","type":"text","marks":[{"type":"code_inline"}]},{"text":", capture exact output and verify by rerun","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"act fails with Docker/runtime error","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/act_usage.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Check Troubleshooting section","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"act fails but workflow works on GitHub","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/act_usage.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Read Limitations section","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"User asks about actionlint config","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/actionlint_usage.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Provide examples","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"User asks about act options","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/act_usage.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Read Advanced Options","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Security vulnerability detected","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/common_errors.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Quote minimal safe fix snippet","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Validating action versions","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/action_versions.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Check version table and offline note","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Using modern features","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/modern_features.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Check syntax examples","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Runner questions/errors","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/runners.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Check labels and availability","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Script Output to Reference Mapping","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Output Pattern","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Reference File","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"[syntax-check]","type":"text","marks":[{"type":"code_inline"}]},{"text":", parse, YAML errors","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"common_errors.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Syntax Errors","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"[expression]","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"${{","type":"text","marks":[{"type":"code_inline"}]},{"text":", condition parsing","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"common_errors.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Expression Errors","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"[action]","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"uses:","type":"text","marks":[{"type":"code_inline"}]},{"text":", input/output mismatch","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"common_errors.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Action Errors","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"[events]","type":"text","marks":[{"type":"code_inline"}]},{"text":" with CRON/schedule text","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"common_errors.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Schedule Errors","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"potentially untrusted","type":"text","marks":[{"type":"code_inline"}]},{"text":", injection warnings","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"common_errors.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Security section","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"[runner-label]","type":"text","marks":[{"type":"code_inline"}]},{"text":" or unknown ","type":"text"},{"text":"runs-on","type":"text","marks":[{"type":"code_inline"}]},{"text":" label","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"runners.md","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"[job-needs]","type":"text","marks":[{"type":"code_inline"}]},{"text":" dependency errors","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"common_errors.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Job Configuration Errors","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"[glob]","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"paths","type":"text","marks":[{"type":"code_inline"}]},{"text":", pattern errors","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"common_errors.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Path Filter Errors","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Docker/pull/image errors from act","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"act_usage.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Troubleshooting","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"No pattern match","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"common_errors.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" + official docs (label ","type":"text"},{"text":"UNMAPPED","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Reference Files Summary","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"File","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Content","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/act_usage.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Act tool usage, commands, options, limitations, troubleshooting","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/actionlint_usage.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Actionlint validation categories, configuration, integration","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/common_errors.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Common errors catalog with fixes","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/action_versions.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Current action versions, deprecation timeline, SHA pinning","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/modern_features.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Reusable workflows, SBOM, OIDC, environments, containers","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/runners.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"GitHub-hosted runners (ARM64, GPU, M2 Pro, deprecations)","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Troubleshooting","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Issue","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Solution","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"Tools not found\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Run ","type":"text"},{"text":"bash \"$SKILL_DIR/scripts/install_tools.sh\"","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"Docker daemon not running\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Start Docker or use ","type":"text"},{"text":"--lint-only","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"Permission denied\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Run ","type":"text"},{"text":"chmod +x \"$SKILL_DIR\"/scripts/*.sh","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"act fails but GitHub works","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"See ","type":"text"},{"text":"references/act_usage.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" Limitations","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Debug Mode","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"actionlint -verbose .github/workflows/ci.yml # Verbose actionlint\nact -v # Verbose act\nact -n # Dry-run (no execution)","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Best Practices","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Always validate locally first","type":"text","marks":[{"type":"strong"}]},{"text":" - Catch errors before pushing","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use actionlint in CI/CD","type":"text","marks":[{"type":"strong"}]},{"text":" - Automate validation in pipelines","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Pin action versions","type":"text","marks":[{"type":"strong"}]},{"text":" - Use ","type":"text"},{"text":"@v6","type":"text","marks":[{"type":"code_inline"}]},{"text":" not ","type":"text"},{"text":"@main","type":"text","marks":[{"type":"code_inline"}]},{"text":" for stability; SHA pinning for security","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Keep tools updated","type":"text","marks":[{"type":"strong"}]},{"text":" - Regularly update actionlint and act","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use official docs for unknown actions","type":"text","marks":[{"type":"strong"}]},{"text":" - Verify usage and versions","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Check version compatibility","type":"text","marks":[{"type":"strong"}]},{"text":" - See ","type":"text"},{"text":"references/action_versions.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Enable shellcheck","type":"text","marks":[{"type":"strong"}]},{"text":" - Catch shell script issues early","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Review security warnings","type":"text","marks":[{"type":"strong"}]},{"text":" - Address script injection issues","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Limitations","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"act limitations","type":"text","marks":[{"type":"strong"}]},{"text":": Not all GitHub Actions features work locally","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Docker requirement","type":"text","marks":[{"type":"strong"}]},{"text":": act requires Docker to be running","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Network actions","type":"text","marks":[{"type":"strong"}]},{"text":": Some GitHub API actions may fail locally","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Private actions","type":"text","marks":[{"type":"strong"}]},{"text":": Cannot validate without access","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Runtime behavior","type":"text","marks":[{"type":"strong"}]},{"text":": Static analysis cannot catch all issues","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"File location","type":"text","marks":[{"type":"strong"}]},{"text":": act can only validate workflows in ","type":"text"},{"text":".github/workflows/","type":"text","marks":[{"type":"code_inline"}]},{"text":" directory; files outside (like ","type":"text"},{"text":"examples/","type":"text","marks":[{"type":"code_inline"}]},{"text":") can only be validated with actionlint","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Quick Examples","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Example 1: Pre-commit Validation","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"SKILL_DIR=\"devops-skills-plugin/skills/github-actions-validator\"\nbash \"$SKILL_DIR/scripts/validate_workflow.sh\" .github/workflows/\ngit add .github/workflows/ && git commit -m \"Update workflows\"","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Example 2: Debug Failing Workflow","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"bash \"$SKILL_DIR/scripts/validate_workflow.sh\" --lint-only .github/workflows/failing.yml\n# Fix issues\nbash \"$SKILL_DIR/scripts/validate_workflow.sh\" .github/workflows/failing.yml","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Complete Worked Example: Multi-Error Workflow","type":"text"}]},{"type":"paragraph","content":[{"text":"This example demonstrates the ","type":"text"},{"text":"full assistant workflow","type":"text","marks":[{"type":"strong"}]},{"text":" for handling multiple errors.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"User's Problematic Workflow","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"name: Broken CI\non:\n schedule:\n - cron: '0 0 * * 8' # ERROR 1\njobs:\n build:\n runs-on: ubuntu-lastest # ERROR 2\n steps:\n - uses: actions/checkout@v3 # ERROR 3 (outdated)\n - run: echo ${{ github.event.issue.title }} # ERROR 4 (security)\n deploy:\n needs: biuld # ERROR 5 (typo)\n runs-on: ubuntu-latest\n steps:\n - run: echo \"Deploying\"","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 1: Run Validation","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"bash \"$SKILL_DIR/scripts/validate_workflow.sh\" --lint-only workflow.yml","type":"text"}]},{"type":"paragraph","content":[{"text":"Output:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"[ERROR] invalid CRON format \"0 0 * * 8\"\n[ERROR] label \"ubuntu-lastest\" is unknown\n[WARN] \"github.event.issue.title\" is potentially untrusted\n[ERROR] job \"deploy\" needs job \"biuld\" which does not exist","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 2-3: Consult References and Quote Fixes","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"paragraph","content":[{"text":"Error 1: Invalid CRON Expression","type":"text","marks":[{"type":"strong"}]}]},{"type":"paragraph","content":[{"text":"From ","type":"text","marks":[{"type":"em"}]},{"text":"references/common_errors.md","type":"text","marks":[{"type":"code_inline"},{"type":"em"}]},{"text":" - Schedule Errors:","type":"text","marks":[{"type":"em"}]}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"Cause:","type":"text","marks":[{"type":"strong"}]},{"text":" Day of week 8 doesn't exist (valid: 0-6, where 0 = Sunday)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"# Bad\nschedule:\n - cron: '0 0 * * 8' # Day 8 doesn't exist\n\n# Good\nschedule:\n - cron: '0 0 * * 0' # Sunday","type":"text"}]},{"type":"paragraph","content":[{"text":"CRON format:","type":"text","marks":[{"type":"strong"}]},{"text":" ","type":"text"},{"text":"minute hour day month weekday","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Weekday: 0-6 (0 = Sunday)","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"Fix:","type":"text","marks":[{"type":"strong"}]},{"text":" Change ","type":"text"},{"text":"8","type":"text","marks":[{"type":"code_inline"}]},{"text":" to ","type":"text"},{"text":"0","type":"text","marks":[{"type":"code_inline"}]},{"text":" (Sunday) or ","type":"text"},{"text":"1-6","type":"text","marks":[{"type":"code_inline"}]},{"text":" for Monday-Saturday.","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"paragraph","content":[{"text":"Error 2: Invalid Runner Label","type":"text","marks":[{"type":"strong"}]}]},{"type":"paragraph","content":[{"text":"From ","type":"text","marks":[{"type":"em"}]},{"text":"references/common_errors.md","type":"text","marks":[{"type":"code_inline"},{"type":"em"}]},{"text":" - Job Configuration Errors and ","type":"text","marks":[{"type":"em"}]},{"text":"references/runners.md","type":"text","marks":[{"type":"code_inline"},{"type":"em"}]},{"text":":","type":"text","marks":[{"type":"em"}]}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"Cause:","type":"text","marks":[{"type":"strong"}]},{"text":" Typo in runner label","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"# Bad\nruns-on: ubuntu-lastest # Typo\n\n# Good\nruns-on: ubuntu-latest","type":"text"}]},{"type":"paragraph","content":[{"text":"Valid labels (from ","type":"text","marks":[{"type":"strong"}]},{"text":"references/runners.md","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":"):","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"ubuntu-latest","type":"text","marks":[{"type":"code_inline"}]},{"text":" (Ubuntu 24.04)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"ubuntu-24.04","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"ubuntu-22.04","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"macos-latest","type":"text","marks":[{"type":"code_inline"}]},{"text":" (macOS 15)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"windows-latest","type":"text","marks":[{"type":"code_inline"}]},{"text":" (Windows Server 2022)","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"Fix:","type":"text","marks":[{"type":"strong"}]},{"text":" Change ","type":"text"},{"text":"ubuntu-lastest","type":"text","marks":[{"type":"code_inline"}]},{"text":" to ","type":"text"},{"text":"ubuntu-latest","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"paragraph","content":[{"text":"Error 3: Outdated Action Version","type":"text","marks":[{"type":"strong"}]}]},{"type":"paragraph","content":[{"text":"From ","type":"text","marks":[{"type":"em"}]},{"text":"references/action_versions.md","type":"text","marks":[{"type":"code_inline"},{"type":"em"}]},{"text":":","type":"text","marks":[{"type":"em"}]}]},{"type":"blockquote","content":[{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Action","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Current Version","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Minimum Supported","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"actions/checkout","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"v6","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"v4","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"SHA Pinning Best Practice:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"# Recommended: SHA pinning with version comment\n- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0","type":"text"}]}]},{"type":"paragraph","content":[{"text":"Fix:","type":"text","marks":[{"type":"strong"}]},{"text":" Update from ","type":"text"},{"text":"@v3","type":"text","marks":[{"type":"code_inline"}]},{"text":" to ","type":"text"},{"text":"@v6","type":"text","marks":[{"type":"code_inline"}]},{"text":" (or SHA-pinned version).","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"paragraph","content":[{"text":"Error 4: Security Vulnerability (Script Injection)","type":"text","marks":[{"type":"strong"}]}]},{"type":"paragraph","content":[{"text":"From ","type":"text","marks":[{"type":"em"}]},{"text":"references/common_errors.md","type":"text","marks":[{"type":"code_inline"},{"type":"em"}]},{"text":" - Expression Errors:","type":"text","marks":[{"type":"em"}]}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"Warning:","type":"text","marks":[{"type":"strong"}]},{"text":" Potential script injection via untrusted input","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"# Bad - vulnerable to injection\nrun: echo ${{ github.event.issue.title }}\n\n# Good - use environment variables\nenv:\n TITLE: ${{ github.event.issue.title }}\nrun: echo \"$TITLE\"","type":"text"}]},{"type":"paragraph","content":[{"text":"Why:","type":"text","marks":[{"type":"strong"}]},{"text":" Untrusted input (issue titles, PR bodies, commit messages) can contain malicious commands. Using environment variables sanitizes the input.","type":"text"}]}]},{"type":"paragraph","content":[{"text":"Fix:","type":"text","marks":[{"type":"strong"}]},{"text":" Pass untrusted input through environment variable.","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"paragraph","content":[{"text":"Error 5: Undefined Job Dependency","type":"text","marks":[{"type":"strong"}]}]},{"type":"paragraph","content":[{"text":"From ","type":"text","marks":[{"type":"em"}]},{"text":"references/common_errors.md","type":"text","marks":[{"type":"code_inline"},{"type":"em"}]},{"text":" - Job Configuration Errors:","type":"text","marks":[{"type":"em"}]}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"Error:","type":"text","marks":[{"type":"strong"}]},{"text":" Job 'deploy' depends on job 'biuld' which does not exist","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"# Bad\njobs:\n build:\n runs-on: ubuntu-latest\n deploy:\n needs: biuld # Typo\n\n# Good\njobs:\n build:\n runs-on: ubuntu-latest\n deploy:\n needs: build","type":"text"}]}]},{"type":"paragraph","content":[{"text":"Fix:","type":"text","marks":[{"type":"strong"}]},{"text":" Change ","type":"text"},{"text":"biuld","type":"text","marks":[{"type":"code_inline"}]},{"text":" to ","type":"text"},{"text":"build","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 4: Provide Corrected Workflow","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"name: Fixed CI\non:\n schedule:\n - cron: '0 0 * * 0' # Fixed: Sunday (0-6 valid)\njobs:\n build:\n runs-on: ubuntu-latest # Fixed: typo corrected\n steps:\n - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - Fixed: updated version\n - name: Process issue\n env:\n TITLE: ${{ github.event.issue.title }} # Fixed: use env var\n run: echo \"$TITLE\"\n deploy:\n needs: build # Fixed: typo corrected\n runs-on: ubuntu-latest\n steps:\n - run: echo \"Deploying\"","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 5: Mandatory Rerun","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"bash \"$SKILL_DIR/scripts/validate_workflow.sh\" --lint-only workflow.yml","type":"text"}]},{"type":"paragraph","content":[{"text":"Expected rerun result:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"No previous errors reproduced","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Remaining warnings, if any, are documented explicitly","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 6: Summary","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Error","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Type","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Fix Applied","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"CRON ","type":"text"},{"text":"0 0 * * 8","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Schedule","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Changed to ","type":"text"},{"text":"0 0 * * 0","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"ubuntu-lastest","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Runner","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Changed to ","type":"text"},{"text":"ubuntu-latest","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"checkout@v3","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Outdated Action","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Updated to ","type":"text"},{"text":"@v6.0.0","type":"text","marks":[{"type":"code_inline"}]},{"text":" (SHA-pinned)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Direct ","type":"text"},{"text":"${{ }}","type":"text","marks":[{"type":"code_inline"}]},{"text":" in run","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Security","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Wrapped in environment variable","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"needs: biuld","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Job Dependency","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Changed to ","type":"text"},{"text":"needs: build","type":"text","marks":[{"type":"code_inline"}]}]}]}]}]},{"type":"paragraph","content":[{"text":"Recommendations:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Run ","type":"text"},{"text":"bash \"$SKILL_DIR/scripts/validate_workflow.sh\" --check-versions","type":"text","marks":[{"type":"code_inline"}]},{"text":" regularly","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use SHA pinning for all actions in production workflows","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Always pass untrusted input through environment variables","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Done Criteria","type":"text"}]},{"type":"paragraph","content":[{"text":"Validation work is complete when all are true:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Trigger matched and correct validation mode selected.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Each mapped error includes source reference and minimal quote.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Each unmapped error is labeled ","type":"text"},{"text":"UNMAPPED","type":"text","marks":[{"type":"code_inline"}]},{"text":" with exact output captured.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Public action versions are verified, or marked ","type":"text"},{"text":"UNVERIFIED-OFFLINE","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Post-fix rerun executed and result reported.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Summary","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Setup","type":"text","marks":[{"type":"strong"}]},{"text":": Install tools with ","type":"text"},{"text":"install_tools.sh","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Validate","type":"text","marks":[{"type":"strong"}]},{"text":": Run ","type":"text"},{"text":"validate_workflow.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" on workflow files","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Fix","type":"text","marks":[{"type":"strong"}]},{"text":": Address issues using reference documentation","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Rerun","type":"text","marks":[{"type":"strong"}]},{"text":": Verify fixes with a mandatory post-fix validation run","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Search","type":"text","marks":[{"type":"strong"}]},{"text":": Use official docs to verify unknown actions","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Commit","type":"text","marks":[{"type":"strong"}]},{"text":": Push validated workflows with confidence","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"For detailed information, consult the appropriate reference file in ","type":"text"},{"text":"references/","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},"metadata":{"date":"2026-06-05","name":"github-actions-validator","author":"@skillopedia","source":{"stars":224,"repo_name":"cc-devops-skills","origin_url":"https://github.com/akin-ozer/cc-devops-skills/blob/HEAD/devops-skills-plugin/skills/github-actions-validator/SKILL.md","repo_owner":"akin-ozer","body_sha256":"908ce676925c581a1b709a983f4c14c639214cac2c2899741df7c974d7674a89","cluster_key":"c8355fadb086bcd3b69c9d3e840b04e8bdc4810980b074656cb8eca9a1bd25cd","clean_bundle":{"format":"clean-skill-bundle-v1","source":"akin-ozer/cc-devops-skills/devops-skills-plugin/skills/github-actions-validator/SKILL.md","attachments":[{"id":"33e71699-6fdc-5ce2-b8a8-34bc99cd271f","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/33e71699-6fdc-5ce2-b8a8-34bc99cd271f/attachment","path":".gitignore","size":50,"sha256":"0f8e82cc17fd83c40649559cce1c063324a5b7bc44c866538a2b335f1d82ce04","contentType":"text/plain; charset=utf-8"},{"id":"859219eb-05de-5a2b-8549-f4cdf79a9a6b","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/859219eb-05de-5a2b-8549-f4cdf79a9a6b/attachment.md","path":"examples/README.md","size":2224,"sha256":"6c9eb2a31f982f0d64e5862f82172f83f37cc51df014796c23b3697cc3855b26","contentType":"text/markdown; charset=utf-8"},{"id":"6e34bf99-bee8-5f85-8ff2-6ee4a849079a","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/6e34bf99-bee8-5f85-8ff2-6ee4a849079a/attachment.yml","path":"examples/outdated-versions.yml","size":1934,"sha256":"8dc482f5e867d4195e818e2ae17278c38479833c86a8b245b7b98841b8eb8d19","contentType":"application/yaml; charset=utf-8"},{"id":"5628d247-6189-54ee-9384-d858070bdb87","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/5628d247-6189-54ee-9384-d858070bdb87/attachment.yml","path":"examples/valid-ci.yml","size":1843,"sha256":"8eb9a6a12405d79e130237b0c8293d5fb1fa3c6b804870efbbf983082eb5da9d","contentType":"application/yaml; charset=utf-8"},{"id":"aabd3cbd-8a53-51e5-a05f-5ff074383491","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/aabd3cbd-8a53-51e5-a05f-5ff074383491/attachment.yml","path":"examples/with-errors.yml","size":1385,"sha256":"219dcd3628cc7f0bcd1d777216f7194363c0429bf148f69b4da009798b7cbd63","contentType":"application/yaml; charset=utf-8"},{"id":"ef078561-330b-5738-86ad-e383f7e40c1f","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/ef078561-330b-5738-86ad-e383f7e40c1f/attachment.md","path":"references/act_usage.md","size":4858,"sha256":"e084aac424e4153203ea30c6b76a435ca0ca376491fd82b4606cb29c7440b13f","contentType":"text/markdown; charset=utf-8"},{"id":"f8f658ee-9c56-53eb-bea2-c4971a7486e3","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/f8f658ee-9c56-53eb-bea2-c4971a7486e3/attachment.md","path":"references/action_versions.md","size":4569,"sha256":"3277b776935f2d29a620ff67fdaa233c5a844c2b6333d64a41e6afe0f119327a","contentType":"text/markdown; charset=utf-8"},{"id":"e6029273-caf0-5683-94dd-4ac980da0bc1","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/e6029273-caf0-5683-94dd-4ac980da0bc1/attachment.md","path":"references/actionlint_usage.md","size":7119,"sha256":"9019ecc43223200b93435422df5b530c10825e62b2a5acd6412b9ff11dcdc195","contentType":"text/markdown; charset=utf-8"},{"id":"21a0de37-d90f-525b-b445-9353178bf1a8","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/21a0de37-d90f-525b-b445-9353178bf1a8/attachment.md","path":"references/common_errors.md","size":9280,"sha256":"200256a62ec58e07221f84bb0af644e4dd775d0a15f2c1284a96afdd8fc74dbd","contentType":"text/markdown; charset=utf-8"},{"id":"56a920e0-21f0-5f59-9ef3-ffb704e5758c","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/56a920e0-21f0-5f59-9ef3-ffb704e5758c/attachment.md","path":"references/modern_features.md","size":8766,"sha256":"cacdf2968f73327fd9dee098d09c480d2dfe8ab85a9c53d2521cf665a73dc838","contentType":"text/markdown; charset=utf-8"},{"id":"97238b6c-c9ea-50b7-b773-bbb550b5bf1e","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/97238b6c-c9ea-50b7-b773-bbb550b5bf1e/attachment.md","path":"references/runners.md","size":7642,"sha256":"794dde89fa077e6f1e57f28635a1eec3f5321cb6ef0109a6ed1804898078fab9","contentType":"text/markdown; charset=utf-8"},{"id":"c355245f-1499-55d4-ba2c-08f3ee2cac0f","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/c355245f-1499-55d4-ba2c-08f3ee2cac0f/attachment.sh","path":"scripts/install_tools.sh","size":3186,"sha256":"831fe16a6773e4c97b6f59132e273ebd38e97cf6cb4549f5d0b4d88e0ab794b2","contentType":"application/x-sh; charset=utf-8"},{"id":"4bd4fbaf-4ac0-5567-a4c4-c338a3abe26e","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/4bd4fbaf-4ac0-5567-a4c4-c338a3abe26e/attachment.sh","path":"scripts/validate_workflow.sh","size":33091,"sha256":"d1078eb9946e569376550111ba97473ea64ae70b5bea3c58d0400c01bb582e02","contentType":"application/x-sh; charset=utf-8"},{"id":"53a69c35-6cd1-5ec9-935f-e9bbd1bc482b","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/53a69c35-6cd1-5ec9-935f-e9bbd1bc482b/attachment.sh","path":"tests/test_validate_workflow.sh","size":6914,"sha256":"c1622b418f22e378543a02b1b9e5bbc55343c3f4a17a7a440f0b811c6c12fa03","contentType":"application/x-sh; charset=utf-8"}],"bundle_sha256":"0b277a301625bc281cc79d9d8394291e8dbd79e877784acb25a50bbc8decc83a","attachment_count":14,"text_attachments":13,"attachment_storage":"skillopedia-attachments-v1","binary_attachments":1,"excluded_attachments":[]},"cluster_size":1,"skill_md_path":"devops-skills-plugin/skills/github-actions-validator/SKILL.md","import_metadata":{"date":"2026-06-05","author":"@skillopedia","version":"v1","category":"security","category_label":"Security"},"exact_dupes_collapsed_into_this":0},"version":"v1","category":"security","import_tag":"clean-skills-v1","description":"Validate, lint, audit, fix GitHub Actions workflows (.github/workflows)."}},"renderedAt":1782979280525}

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. Trigger Phrases Use this skill when the request includes phrases like: - "validate this GitHub Actions workflow" - "check my file" - "debug actionlint errors" - "test this workflow locally with act" - "verify GitHub Action versions or deprecation…