actionlint checks workflow structure before GitHub runs it
actionlint reads files under .github/workflows and reports mistakes with file, line, column, source context, and a named rule. Its scope goes beyond YAML parsing. It understands GitHub's workflow keys, expression types, job dependencies, matrix values, runner labels, reusable workflow inputs, action inputs, glob patterns, and cron expressions. It also flags untrusted event values inserted directly into shell commands and hard-coded credentials.
That breadth makes it useful in an editor and in pull-request CI. A misspelled node-version input, undefined matrix property, invalid runner label, or wrong event key can be caught before a remote runner starts. ShellCheck and pyflakes can inspect embedded Bash and Python when those tools are installed. Diagnostics can be formatted for reviewdog and problem matchers, while the Go package is available to applications that want to embed checking.
The 17-second build was cleaner than the full test result
Our sandbox cloned commit 011a6d1 and installed 18 Go packages in 9 seconds. The build completed successfully in 17 seconds. The checkout contained 850 files, about 34,778 lines of source, and 3.6 MB before installation. This is a small local tool compared with the CI systems it checks, and the repository includes a Dockerfile plus 6 workflow files.
The package can also be installed as a release binary, through Homebrew and other package managers, with go install, or as a Docker image. An online WebAssembly playground provides a no-install trial. Normal local linting needs no GitHub token. Remote action metadata and surrounding CI integrations may add network or configuration requirements, so a locked-down environment should test the exact flags it plans to use.
What happened when we ran it
Our Go test command ended with exit code 1 after 7 seconds. The harness counted 4 passing groups and 1 failure out of 5. The failure was TestDetectErrorBadRequest under scripts/generate-popular-actions: the test expected exit status 1 and received 0. Other listed packages, including the main github.com/rhysd/actionlint package, passed in the supplied log.
The log does not say why that command returned 0, so we cannot label it a network problem or a product regression. It is still a failed complete suite at the measured commit. Our scan found no top-level tests directory, which is normal for Go packages that keep _test.go files beside source. These results come from an unprivileged Go 1.24 Debian container with 3 CPUs, 6 GB of RAM, and no secrets.
Workflow linting stops before action metadata
The tool checks how a workflow invokes an action, including whether with inputs and referenced outputs match known metadata. It does not lint the action's own action.yml or action.yaml definition. Issue 46 has asked for action metadata support since composite actions were new. Teams that build private JavaScript, Docker, or composite actions therefore need another schema check for those files.
Execution is another boundary. actionlint can reason about syntax and types, but it does not run containers, contact services, prove credentials work, or reproduce runner-specific behavior. nektos/act covers part of that local execution need, while a real GitHub Actions run remains the final environment check. Keeping these stages separate avoids treating a clean static result as proof that deployment permissions or external APIs are correct.
GitHub syntax can move faster than the bundled rules
GitHub adds events, runner labels, expression properties, and permission names over time. Issue 726 reports actionlint rejecting the destroyed activity for merge_group, even though GitHub documents it for the webhook. Open requests also cover the copilot-requests, code-quality, and vulnerability-alerts permission keys. A valid new workflow can therefore fail lint until the project updates its definitions.
The practical response is to pin actionlint, review a new diagnostic against current GitHub documentation, and use the narrowest ignore possible when the tool is behind. Broadly suppressing an entire file throws away the checks that still work. Custom self-hosted runner labels and repository variables belong in actionlint.yaml; without that project configuration, local names can look invalid even when GitHub accepts them.
Security checks are useful but not a full policy audit
One of actionlint's best rules detects potentially untrusted values, such as commit messages, embedded directly into an inline script. Moving the expression into an environment variable prevents the shell from parsing attacker-controlled text as code. Hard-coded credential detection adds another useful check, and ShellCheck can find mistakes inside the resulting script. These are concrete protections that fit naturally into every workflow change.
Issue 728 asks for a warning when a workflow omits a top-level permissions block. That check is not present today, so a clean actionlint run does not prove least privilege. Zizmor is the better companion when permissions, dangerous triggers, mutable action references, and other workflow security rules drive the review. Neither tool can decide whether a granted cloud role is too broad outside GitHub.
Community work continued after the July push
GitHub showed 4,172 stars, 187 combined issues and pull requests, and a last repository push on July 16, 2026. Release v1.7.12 was published on March 30. Issue and pull request activity continued through August 26, including new permissions, event types, dependency updates, runner labels, and popular-action data. The repository is not devoid of work, but merged source activity trails the incoming queue.
Issue 719 directly asks whether the author is inactive and whether an active fork exists. That short report is a community concern, not proof of abandonment. The sensible adoption plan is to pin v1.7.12, watch compatibility with GitHub's evolving syntax, and avoid depending on an unmerged feature. Even with that caveat, actionlint catches enough cheap mistakes to justify a place before every workflow commit.

