Actionlint and ShellCheck: catch CI errors before they run
Added 2026-10-11. Requires actionlint, ShellCheck and pre-commit to be installed; pin the versions in the developer environment and in CI.
Problem
A plain YAML linter doesn't notice an invalid GitHub Actions key or a reference to a non-existent matrix variable. A shell script passes a simple syntax check but breaks on a path with spaces.
Solution
actionlint is "a static checker for GitHub Actions workflow files": a syntax check against the workflow syntax, a strong type check for ${{ }} expressions, checks of action inputs/outputs, and security checks for script injection by untrusted inputs and hard-coded credentials. It integrates with ShellCheck for scripts at run:.
ShellCheck is "a shell script static analysis tool" that "gives warnings and suggestions for bash/sh shell scripts". It needs a separate run for script files: the contents of a called ./scripts/deploy.sh do not automatically become part of the workflow check.
Example
Add to the existing repos list in .pre-commit-config.yaml:
repos:
- repo: local
hooks:
- id: actionlint
name: Check GitHub Actions
entry: actionlint
language: system
files: '^\.github/workflows/.*\.ya?ml$'
- id: shellcheck
name: Check shell scripts
entry: shellcheck
language: system
types: [shell]
pre-commit run actionlint --all-files
pre-commit run shellcheck --all-files
Without pre-commit, check the workflows and a specific script directly:
actionlint
shellcheck scripts/deploy.sh
An expression in a workflow step: event data is passed through an environment variable so the text never becomes shell source code.
- name: Show PR title
env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: printf '%s\n' "$PR_TITLE"
This is a steps fragment, not a full workflow. If the project has no GitHub Actions, keep ShellCheck for scripts: actionlint does not check GitLab CI. Write linter exceptions for a specific case with an explanation, without disabling checks globally.
When to use
When changing workflows, backup and deploy scripts. Complements pre-commit for IaC.
Source
How to apply it yourself
- Check all tracked workflows and shell files, work through the current findings.
- Wire the same checks into CI so the local hook can't be bypassed by accident.
- In a test branch, add an unknown workflow key and an unquoted variable with spaces; make sure the checks catch the errors.
- After the fix and a green CI, mark it "Studied".