Skip to content

Comments

chore: replace yq-based workflow validation with actionlint#381

Merged
stack72 merged 1 commit intomainfrom
simplify-workflow-validation
Feb 18, 2026
Merged

chore: replace yq-based workflow validation with actionlint#381
stack72 merged 1 commit intomainfrom
simplify-workflow-validation

Conversation

@stack72
Copy link
Contributor

@stack72 stack72 commented Feb 18, 2026

Summary

This PR replaces the hand-rolled yq-based workflow validation with actionlint, a proper static analysis tool for GitHub Actions workflows.

Why the old approach was insufficient

The previous workflow-validation.yml (227 lines) performed shallow checks using yq and grep:

  • YAML syntax validation — redundant, because GitHub already validates workflow YAML natively when you push. If a workflow has invalid YAML, GitHub surfaces the error immediately without needing a separate CI job.
  • Structural field checks (name, on, jobs, runs-on, steps) — these are the most basic possible checks. They don't validate that the values are correct, just that the keys exist.
  • Deprecated action version warnings — only checked for actions/checkout@v1/v2 and actions/setup-node@v1/v2. Easy to miss other stale actions.
  • Noisy/incorrect warnings — warned about workflows that have both push and pull_request triggers, which is a completely normal and common pattern. Also warned that workflow_dispatch was absent, which is advisory at best. The old workflow itself had both push and pull_request triggers, so it would warn about itself.
  • Superficial secret detection — the grep patterns used were easy to bypass and produced false positives.

Why actionlint is better

actionlint is a purpose-built static checker for GitHub Actions workflows. It provides:

  • Type-checking of ${{ }} expressions — catches invalid context references, wrong types, undefined variables
  • Action input/output validation — verifies that inputs passed to actions actually exist and are the correct type, based on each action's action.yml schema
  • needs dependency validation — catches references to jobs that don't exist
  • Runner label validation — flags invalid or unknown runner labels
  • Shell script analysis — integrates with shellcheck to lint run: steps
  • Glob syntax validation
  • Cron expression validation

What changed

  • Removed all yq-based validation steps
  • Added two steps: download actionlint using rhysd's official download script, then run it
  • Added workflow_dispatch trigger for manual runs
  • 227 lines → 30 lines

Approach

Used rhysd's recommended download script approach rather than a third-party action wrapper, to avoid a dependency on an external maintainer keeping a wrapper action up to date.

🤖 Generated with Claude Code

@stack72 stack72 force-pushed the simplify-workflow-validation branch from f6eea09 to eafbbb5 Compare February 18, 2026 21:35
github-actions[bot]
github-actions bot previously approved these changes Feb 18, 2026
Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: Approved ✅

This PR makes an excellent improvement to workflow validation by replacing a custom 227-line yq-based implementation with actionlint, a purpose-built static analysis tool.

No Blocking Issues

This is a CI workflow change, not application code, so the TypeScript requirements from CLAUDE.md (strict mode, named exports, unit tests, license headers) don't apply.

What's Good

  1. Better tooling: actionlint provides comprehensive validation that the custom script couldn't match:

    • Type-checking of ${{ }} expressions
    • Action input/output validation against action.yml schemas
    • Job dependency validation
    • Integration with shellcheck for run: steps
  2. Reduced maintenance: 227 lines → 30 lines with better coverage

  3. Removed false positives: The old script warned about legitimate patterns (e.g., having both push and pull_request triggers)

  4. Security: Uses the official download script from rhysd's repo rather than a third-party wrapper action, minimizing supply chain risk. The workflow appropriately has contents: read permissions only.

  5. Added workflow_dispatch: Enables manual testing of the validation workflow itself

Minor Notes (Non-blocking)

  • The bash <(curl ...) pattern is standard for actionlint installation and documented in their official usage guide
  • The download script correctly sets the executable output that the next step references

Well-documented PR with clear rationale. Ship it!

This PR replaces the hand-rolled yq-based workflow validation with
actionlint (https://github.com/rhysd/actionlint), a proper static
analysis tool for GitHub Actions workflows.

Why the old approach was insufficient

The previous workflow-validation.yml (227 lines) performed shallow
checks using yq and grep:

- YAML syntax validation: redundant, because GitHub already validates
  workflow YAML natively when you push. If a workflow has invalid YAML,
  GitHub surfaces the error immediately without needing a separate CI job.
- Structural field checks (name, on, jobs, runs-on, steps): these are
  the most basic possible checks. They don't validate that the values are
  correct, just that the keys exist.
- Deprecated action version warnings: only checked for
  actions/checkout@v1/v2 and actions/setup-node@v1/v2. Easy to miss
  other stale actions.
- Noisy/incorrect warnings: warned about workflows that have both push
  and pull_request triggers, which is a completely normal and common
  pattern. Also warned that workflow_dispatch was absent, which is
  advisory at best. The old workflow itself had both push and
  pull_request triggers, so it would warn about itself.
- Superficial secret detection: the grep patterns used were easy to
  bypass and produced false positives.

Why actionlint is better

actionlint is a purpose-built static checker for GitHub Actions
workflows. It provides:

- Type-checking of ${{ }} expressions: catches invalid context
  references, wrong types, undefined variables
- Action input/output validation: verifies that inputs passed to actions
  actually exist and are the correct type, based on each action's
  action.yml schema
- needs dependency validation: catches references to jobs that don't exist
- Runner label validation: flags invalid or unknown runner labels
- Shell script analysis: integrates with shellcheck to lint run: steps
- Glob syntax validation
- Cron expression validation

What changed

- Removed all yq-based validation steps
- Added two steps: download actionlint using rhysd's official download
  script, then run it
- Added workflow_dispatch trigger for manual runs
- 227 lines to 30 lines

Used rhysd's recommended download script approach rather than a
third-party action wrapper, to avoid a dependency on an external
maintainer keeping a wrapper action up to date.
Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Summary

This PR replaces 227 lines of hand-rolled yq-based workflow validation with actionlint, a purpose-built static analyzer for GitHub Actions. This is a clear improvement.

Blocking Issues

None.

Observations

Security: The approach uses bash <(curl ...) to download actionlint, which is the officially documented method. While this pattern has inherent supply-chain considerations, the permissions are properly locked down to contents: read, and this is the maintainer-recommended approach vs. depending on third-party wrapper actions.

Code Quality: The change reduces complexity from 227 lines to 30 lines while gaining significantly more robust validation (expression type-checking, action input/output validation, needs dependency validation, shellcheck integration, etc.).

Rationale: The PR description clearly explains why the old approach was insufficient:

  • Redundant YAML validation (GitHub already does this)
  • Shallow structural checks that only verified key existence
  • Limited deprecated action detection
  • False positive warnings (e.g., warning about push + pull_request triggers which is a common pattern)
  • Superficial secret detection with easy bypasses

LGTM!

@stack72 stack72 merged commit 56db819 into main Feb 18, 2026
2 checks passed
@stack72 stack72 deleted the simplify-workflow-validation branch February 18, 2026 21:38
Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Summary

This PR is a good improvement that replaces 227 lines of hand-rolled yq-based validation with actionlint, a purpose-built static analyzer for GitHub Actions workflows. The changes are well-motivated and well-documented in the PR description.

No blocking issues found.

What was reviewed

.github/workflows/workflow-validation.yml

  • ✅ Correctly replaces verbose manual validation with actionlint
  • ✅ Adds workflow_dispatch for manual testing
  • ✅ Uses official actionlint download script from rhysd's repository

.github/workflows/issue-planner.yml

  • ✅ Shell quoting fixes ($GITHUB_OUTPUT"$GITHUB_OUTPUT") follow best practices
  • ✅ Grouped command for multi-line heredoc is cleaner and more correct

Suggestions (non-blocking)

  1. Consider pinning actionlint download script: Currently downloads from main branch. For maximum security, you could pin to a specific commit hash. However, the current approach is the officially recommended method and is acceptable.

  2. Minor: The download step could potentially fail silently if the curl fails. Consider adding set -euo pipefail or checking the exit code. This is not critical since the subsequent Run actionlint step would fail anyway.

Notes

  • DDD review: N/A - these are CI workflow files, not domain code
  • Test coverage: N/A - workflow changes are tested by CI itself
  • CLAUDE.md compliance: N/A - no TypeScript code modified

LGTM! 🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant