Skip to content

/commands/github-actions/actionlint

actionlint: lint GitHub Actions workflows before you push

verified onenvironmentlocal · windows 10.0.26200 arm64 · GNU bash 5.3.15 (Git Bash) · 1.7.12updated 2026-10-20
tool
GitHub Actions
versions
actionlint 1.7
os
any

GitHub only validates a workflow when it runs. actionlint, an open-source linter, checks workflow files locally: YAML structure, ${{ }} expressions and their types, references to undefined matrix or steps values, untrusted inputs used in run: scripts, deprecated workflow commands and actions too old for current runners. Run it in the repository root; it finds .github/workflows/*.yml by itself.

One file with four problems

bash
mkdir -p .github/workflows
cat > .github/workflows/ci.yml <<'EOF'
name: CI
on: pull_request
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - id: v
        run: echo "::set-output name=ver::1.0"
      - run: echo "${{ steps.v.outputs.ver }} ${{ github.event.pull_request.title }}"
      - run: echo ${{ matrix.node }}
      - uses: actions/setup-node@v1
EOF
actionlint -version | head -1
actionlint -no-color .github/workflows/ci.yml

Output · exit 1

1.7.12
.github/workflows/ci.yml:9:14: workflow command "set-output" was deprecated. use `echo "{name}={value}" >> $GITHUB_OUTPUT` instead: https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions [deprecated-commands]
  |
9 |         run: echo "::set-output name=ver::1.0"
  |              ^~~~
.github/workflows/ci.yml:10:51: "github.event.pull_request.title" is potentially untrusted. avoid using it directly in inline scripts. instead, pass it through an environment variable. see https://docs.github.com/en/actions/reference/security/secure-use#good-practices-for-mitigating-script-injection-attacks for more details [expression]
   |
10 |       - run: echo "${{ steps.v.outputs.ver }} ${{ github.event.pull_request.title }}"
   |                                                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.github/workflows/ci.yml:11:23: property "node" is not defined in object type {} [expression]
   |
11 |       - run: echo ${{ matrix.node }}
   |                       ^~~~~~~~~~~
.github/workflows/ci.yml:12:15: the runner of "actions/setup-node@v1" action is too old to run on GitHub Actions. update the action's version to fix this issue [action]
   |
12 |       - uses: actions/setup-node@v1
   |               ^~~~~~~~~~~~~~~~~~~~~

Each finding has a rule name in brackets. [expression] on pull_request.title is a script injection risk: a PR title like "; curl … | sh; " would run in your job. Pass it through env: instead:

yaml
- run: echo "$TITLE"
  env:
    TITLE: ${{ github.event.pull_request.title }}

Run it in CI

yaml
- name: actionlint
  run: |
    bash <(curl -fsSL https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash)
    ./actionlint -color

When shellcheck is installed, actionlint also checks the shell code in run: blocks.

Sources