actionlint: Static Check GitHub Actions Workflows
actionlint parses every workflow under .github/workflows and reports syntax, expression, and embedded-shell errors before the workflow ever runs.
Broken workflow YAML normally fails only after a push, wasting a run. actionlint catches it locally or in a PR job in milliseconds, and it runs shellcheck on your run: blocks too.
What it does
actionlint reads workflow files, validates them against the Actions schema, type-checks ${{ }} expressions, checks runs-on labels, and pipes each run: script through shellcheck and each ${{ }} through pyflakes when available. Given no arguments it auto-discovers .github/workflows/*.yml.
Common usage
actionlint # lint all workflows in .github/workflows
actionlint .github/workflows/ci.yml # lint one file
actionlint -shellcheck= # disable the shellcheck integration
actionlint -format '{{json .}}' # machine-readable outputOptions
| Flag | What it does |
|---|---|
| -shellcheck <path> | Path to the shellcheck executable; set empty to disable |
| -pyflakes <path> | Path to pyflakes; set empty to disable |
| -format <template> | Go-template output, e.g. {{json .}} or the SARIF template |
| -ignore <regex> | Ignore errors whose message matches the regex |
| -config-file <path> | Use a specific actionlint.yaml config |
| -color | Force colored output |
| -version | Print the version |
In CI
Run actionlint as a required PR check so no workflow change merges with a schema or expression error. The maintained raven-actions/actionlint and reviewdog/action-actionlint marketplace actions wrap it and annotate the PR diff.
Common errors in CI
Findings look like .github/workflows/ci.yml:12:9: property "step" is not defined in object type ..., unexpected key "job" for "workflow" section and label "ubuntu-latest-8core" is unknown. available labels are .... "command not found: actionlint" means it is not installed; install the binary or use the marketplace action. A nonzero exit fails the job even if only one file has findings.