.git-hooks
check all before `git push`
.github
Bump codecov/codecov-action from 5 to 6 in the github-actions group
Casks
Brew cask update for actionlint version v1.7.12
cmd
include timezone database in executable statically
docs
fix section title of the check for `on.schedule` is outdated
fuzz
upgrade yaml/go-yaml from v3 to v4
man
bump up version to v1.7.12
playground
bump up version to v1.7.12
scripts
update version to 1.7.12 in download-actionlint.bash
testdata
Add support for entrypoint and command for service containers
.codecov.yaml
take coverage on codecov
.gitattributes
Revert "add support for Windows to update-checks-doc script"
.gitignore
add `make` task to generate code coverage report
.goreleaser.yaml
fix deprecated GoReleaser config
.pre-commit-hooks.yaml
bump up version to v1.7.12
action_metadata_test.go
test checking unknown keys in inputs in action metadata
action_metadata.go
fix `treosh/lighthouse-ci-action` causes metadata parse error on popular actions codegen
all_webhooks_test.go
generate all_webhooks.go and add tests for the generated variable
all_webhooks.go
generate webhook types table by scraping from the latest document
ast_test.go
add doc comment and tests for `ast.ContainsExpression` function
ast.go
Add support for entrypoint and command for service containers
availability_test.go
scrape all contexts map as `AllContexts` global constant
availability.go
add workaround for missing `jobs.<job_id>.snapshot.if` available contexts/special functions
CHANGELOG.md
update changelog for v1.7.12 changes
command_test.go
move all examples to `example_test.go`
command.go
fix version value may be an empty string
config_test.go
upgrade yaml/go-yaml from v3 to v4
config.go
upgrade yaml/go-yaml from v3 to v4
CONTRIBUTING.md
add note for making winpkg package at new releases
doc.go
replace go-yaml/yaml with yaml/go-yaml (fix #575)
Dockerfile
fix legacy `ENV` format and case sensitivity in Dockerfile
error_test.go
replace `sort` with faster `slices`
error.go
dedup errors duplicated within aliases
example_test.go
extend ExampleLinter comment with the LintStdin
example_your_own_rule_test.go
`node16` reached the end of life
expr_ast.go
track safe function calls instead of checking parents on calling `UntrustedInputChecker.OnVisitNodeLeave`
expr_insecure_test.go
describe safe function calls are ignored on untrusted inputs check
expr_insecure.go
follow the URL change in the official security document
expr_lexer_test.go
replace more `cmp.Equal` calls with `cmp.Diff`
expr_lexer.go
follow expressions document was split from contexts document
expr_parser_test.go
simplify tests with cmp.Diff
expr_parser.go
track safe function calls instead of checking parents on calling `UntrustedInputChecker.OnVisitNodeLeave`
expr_sema_test.go
small improvements for `case()` function call checks
expr_sema.go
small improvements for `case()` function call checks
expr_test.go
check `vars` property names with configuration in actionlint.yaml
expr_type_test.go
replace more `cmp.Equal` calls with `cmp.Diff`
expr_type.go
add tests for JSON typing/validation at `fromJSON` calls
expr.go
implement `String` method to error types for better test failure outputs
glob_test.go
report paths starting with '.' and '..' at path glob (fix #521)
glob.go
report paths starting with '.' and '..' at path glob (fix #521)
go.mod
improve error messages
go.sum
Revert "use goquery instead of x/net/html"
LICENSE.txt
initialize repository
linter_test.go
resolve all aliases before parsing as workflow
linter.go
dedup errors duplicated within aliases
Makefile
fix staticcheck checks files inside `./playground/node_modules`
parse_test.go
add more benchmark for parsing using test data
parse.go
Add support for entrypoint and command for service containers
pass.go
use worker gorutines instead of making goroutine per process
popular_actions_test.go
fix and add tests for generating popular actions data set
popular_actions.go
update popular actions data set to the latest
process_test.go
fix deadlock in shellcheck integration on darwin by using cmd.Stdin
process.go
fix deadlock in shellcheck integration on darwin by using cmd.Stdin
project_test.go
test detecting config file already existing before generating new one
project.go
separate function to create `Project` instance
quotes_test.go
efficiently enumerate sorted quotes
quotes.go
more efficiently serialize YAML array into string
README.md
bump up version to v1.7.12
reusable_workflow_test.go
simplify tests with cmp.Diff
reusable_workflow.go
upgrade yaml/go-yaml from v3 to v4
rule_action.go
fix trailing spaces are not removed when removing newlines from deprecation message
rule_credentials.go
fix `${{ }}` is not available at `jobs.<job_id>.services`
rule_deprecated_commands_test.go
simplify tests with cmp.Diff
rule_deprecated_commands.go
Add a constructor for RuleBase and change error and errorf from private to public, otherwise It's not possible to create new rules
rule_env_var_test.go
call `testing.Helper` in helper functions for testing
rule_env_var.go
fix `${{ }}` is not available at `jobs.<job_id>.services`
rule_events.go
fix timezone check is incomplete
rule_expression.go
Add support for entrypoint and command for service containers
rule_glob.go
check glob patterns in `image_version` trigger and `snapshot` config
rule_id_test.go
implement `String` method to error types for better test failure outputs
rule_id.go
Add a constructor for RuleBase and change error and errorf from private to public, otherwise It's not possible to create new rules
rule_if_cond_test.go
detect more complicated constants in `if:` conditions
rule_if_cond.go
detect more complicated constants in `if:` conditions
rule_job_needs.go
make error message for cycle in `needs:` deterministic
rule_matrix.go
chore: fix typos and style
rule_permissions.go
fix: add artifact-metadata
rule_pyflakes_test.go
add tests for parsing pyflakes output
rule_pyflakes.go
remove redundant newline search on parsing pyflakes output
rule_runner_label_test.go
Add macos-26-intel
rule_runner_label.go
Add macos-26-intel
rule_shell_name.go
Add a constructor for RuleBase and change error and errorf from private to public, otherwise It's not possible to create new rules
rule_shellcheck_test.go
add tests for checking `run:` with shellcheck even if custom shell is used
rule_shellcheck.go
disable SC2153 shellcheck rule to avoid unassigned variable false positive (fix #573)
rule_test.go
add more tests for `RuleBase`
rule_workflow_call_test.go
simplify tests with cmp.Diff
rule_workflow_call.go
Make debug public
rule.go
rename `GetConfig` to `Config` to align getter method names in `RuleBase`
staticcheck.conf
add lib-level doc comment and enable ST1000 statickcheck rule

actionlint

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 in steps.{id}.outputs are 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@v4
      - uses: actions/setup-node@v4
        with:
          node_version: 18.x
      - uses: actions/cache@v4
        with:
          path: ~/.npm
          key: ${{ matrix.platform }}-node-${{ hashFiles('**/package-lock.json') }}
        if: ${{ github.repository.permissions.admin == true }}
      - run: npm install && npm test

actionlint 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", "ubuntu-slim", "ubuntu-latest", "ubuntu-latest-4-cores", "ubuntu-latest-8-cores", "ubuntu-latest-16-cores", "ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "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@v4". available inputs are "always-auth", "architecture", "cache", "cache-dependency-path", "check-latest", "node-version", "node-version-file", "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 }}
   |                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Quick start

Install actionlint command by downloading the released binary or by Homebrew 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/rhysd/actionlint/cmd/actionlint@latest

Basically 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.

actionlint

Another option to try actionlint is the online playground. Your browser can run actionlint through WebAssembly.

See the usage document for more details.

Documents

  • 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 actionlint command 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.

Bug reporting

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.

License

actionlint is distributed under the MIT license.