actionlint is a static checker for GitHub Actions workflow files. Try it online!
Features:
- Syntax check for workflow files to check unexpected or missing keys following workflow syntax
- Strong type check for
${{ }}expressions to catch several semantic errors like access to not existing property, type mismatches, ... - Actions usage check to check that inputs at
with:and outputs insteps.{id}.outputsare correct - Reusable workflow check to check inputs/outputs/secrets of reusable workflows and workflow calls
- shellcheck and pyflakes integrations for scripts at
run: - Security checks; script injection by untrusted inputs, hard-coded credentials
- Other several useful checks; glob syntax validation, dependencies check for
needs:, runner label validation, cron syntax validation, ...
See the full list of checks done by actionlint.
Example of broken workflow:
on:
push:
branch: main
tags:
- 'v\d+'
jobs:
test:
strategy:
matrix:
os: [macos-latest, linux-latest]
runs-on: ${{ matrix.os }}
steps:
- run: echo "Checking commit '${{ github.event.head_commit.message }}'"
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node_version: 18.x
- uses: actions/cache@v6
with:
path: ~/.npm
key: ${{ matrix.platform }}-node-${{ hashFiles('**/package-lock.json') }}
if: ${{ github.repository.permissions.admin == true }}
- run: npm install && npm testactionlint reports 7 errors:
test.yaml:3:5: unexpected key "branch" for "push" section. expected one of "branches", "branches-ignore", "paths", "paths-ignore", "tags", "tags-ignore", "types", "workflows" [syntax-check]
|
3 | branch: main
| ^~~~~~~
test.yaml:5:11: character '\' is invalid for branch and tag names. only special characters [, ?, +, *, \, ! can be escaped with \. see `man git-check-ref-format` for more details. note that regular expression is unavailable. note: filter pattern syntax is explained at https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#filter-pattern-cheat-sheet [glob]
|
5 | - 'v\d+'
| ^~~~
test.yaml:10:28: label "linux-latest" is unknown. available labels are "windows-latest", "windows-latest-8-cores", "windows-2025", "windows-2025-vs2026", windows-2022", "windows-11-arm", "windows-11-vs2026-arm", "ubuntu-slim", "ubuntu-latest", "ubuntu-latest-4-cores", "ubuntu-latest-8-cores", "ubuntu-latest-16-cores", "ubuntu-26.04", "ubuntu-26.04-arm", "ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "xcode-27", "xcode-27-xlarge", "macos-latest", "macos-latest-xlarge", "macos-latest-large", "macos-26-intel", "macos-26-xlarge", "macos-26-large", "macos-26", "macos-15-intel", "macos-15-xlarge", "macos-15-large", "macos-15", "macos-14-xlarge", "macos-14-large", "macos-14", "self-hosted", "x64", "arm", "arm64", "linux", "macos", "windows". if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file [runner-label]
|
10 | os: [macos-latest, linux-latest]
| ^~~~~~~~~~~~~
test.yaml:13:41: "github.event.head_commit.message" 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]
|
13 | - run: echo "Checking commit '${{ github.event.head_commit.message }}'"
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test.yaml:17:11: input "node_version" is not defined in action "actions/setup-node@v7". available inputs are "architecture", "cache", "cache-dependency-path", "check-latest", "mirror", "mirror-token", "node-version", "node-version-file", "package-manager-cache", "registry-url", "scope", "token" [action]
|
17 | node_version: 18.x
| ^~~~~~~~~~~~~
test.yaml:21:20: property "platform" is not defined in object type {os: string} [expression]
|
21 | key: ${{ matrix.platform }}-node-${{ hashFiles('**/package-lock.json') }}
| ^~~~~~~~~~~~~~~
test.yaml:22:17: receiver of object dereference "permissions" must be type of object but got "string" [expression]
|
22 | if: ${{ github.repository.permissions.admin == true }}
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Install actionlint command by downloading the released binary, using the download script, running the Docker
image, using the repository as a GitHub Action, or by go install. See
the installation document for more details like how to manage the command with several package managers
or run via Docker container.
go install github.com/kjanat/actionlint/cmd/actionlint@latestBasically all you need to do is run the actionlint command in your repository. actionlint automatically detects workflows and
checks errors. actionlint focuses on finding out mistakes. It tries to catch errors as much as possible and make false positives
as minimal as possible.
actionlintAnother option to try actionlint is the online playground. Your browser can run actionlint through WebAssembly.
See the usage document for more details.
This repository can be used directly as a Docker action. The prebuilt image includes actionlint, ShellCheck, and pyflakes, and reports problems as GitHub annotations by default. Docker actions require a Linux runner.
name: Lint GitHub Actions workflows
on: [push, pull_request]
jobs:
actionlint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with: { persist-credentials: false }
- uses: kjanat/actionlint@v1The moving v1 tag follows compatible v1 releases. v1.11.0 is a versioned release tag, but only a full-length commit SHA
provides an immutable action reference.
| Input | Default | Description |
|---|---|---|
files |
all workflows | Newline-separated workflow paths. Empty checks every workflow in the repository. |
format |
github |
Output format: github, default, oneline, json, json-lines, markdown, or sarif. |
ignore |
none | Newline-separated regular expressions for actionlint errors to ignore. |
config-file |
automatic | Configuration file path relative to working-directory. |
shellcheck |
true |
Run ShellCheck for shell scripts in workflow steps. |
pyflakes |
true |
Run pyflakes for Python scripts in workflow steps. |
working-directory |
. |
Directory to lint, relative to the repository workspace. |
output-file |
none | Repository-relative file to receive the selected output format. |
fail-on-error |
true |
Fail when problems are found. Invalid options and fatal errors always fail. |
| Output | Description |
|---|---|
exit-code |
actionlint exit code: 0 for clean, 1 for problems, 2 for invalid options, or 3 for failure. |
result |
success, problems-found, invalid-options, or failure. |
problems-found |
Whether actionlint found one or more problems. |
problem-count |
Number of problems, or an empty string if actionlint could not complete. |
output |
Complete actionlint output in the selected format. |
output-file |
Repository-relative output path, or an empty string when no file was requested. |
Give the step an id to consume its outputs. For example, this writes JSON Lines without failing the lint step:
- name: Check workflows
id: actionlint
uses: kjanat/actionlint@v1
with:
format: json-lines
output-file: actionlint-results.jsonl
fail-on-error: false
- name: Report result
if: always()
env:
RESULT: ${{ steps.actionlint.outputs.result }}
PROBLEM_COUNT: ${{ steps.actionlint.outputs.problem-count }}
run: echo "$RESULT ($PROBLEM_COUNT problems)"See the usage document for additional examples and output behavior.
- Checks: Full list of all checks done by actionlint with example inputs, outputs, and playground links.
- Installation: Installation instructions. Prebuilt binaries, a Docker image, building from source, a download script (for CI), supports by several package managers are available.
- Usage: How to use
actionlintcommand locally or on GitHub Actions, the online playground, an official Docker image, and integrations with reviewdog, Problem Matchers, super-linter, pre-commit, VS Code. - Configuration: How to configure actionlint behavior. Currently, the labels of self-hosted runners, the configuration variables, and ignore patterns of errors for each file paths can be set.
- Go API: How to use actionlint as Go library.
- References: Links to resources.
When you see some bugs or false positives, it is helpful to file a new issue with a minimal example of input. Giving me some feedbacks like feature requests or ideas of additional checks is also welcome.
See the contribution guide for more details.
actionlint is distributed under the MIT license.
