Skip to main content

Validate GitHub Actions Workflows with Atmos

· 3 min read
Erik Osterman
Founder @ Cloud Posse

A workflow can be valid YAML and still fail only after GitHub Actions tries to run it: a misspelled trigger filter, an invalid expression, or an action reference that does not make sense in context. Those failures are slow to discover and usually arrive after a push.

Atmos now includes GitHub Actions workflow validation as an experimental native-CI command. Run it from your workstation or in the workflow that it checks:

atmos ci validate

# Equivalent validation-oriented alias
atmos validate ci

Validate Before the Push

With no arguments, atmos ci validate recursively checks every .yml and .yaml file in the current working directory's .github/workflows. It uses the built-in actionlint integration, so the linting capability ships with Atmos rather than requiring a separately installed binary.

A successful run gives a compact confirmation:

✓ Validated 3 GitHub Actions workflow file(s) in .github/workflows.

Findings include the workflow path, line, column, and actionlint rule. They fail the command, which makes validation suitable for both a pre-push check and a CI gate.

Check a Fixture or Another Directory

Sometimes the workflows you want to validate do not live in the current directory's default location — for example, when testing generated workflows or fixtures. Use --workflow-path to point at that directory. Atmos recursively finds workflow YAML beneath it:

atmos ci validate --workflow-path tests/fixtures/scenarios/invalid-github-actions-workflows/.github/workflows

That repository fixture intentionally uses branch instead of branches in a push trigger. The command reports the line and exits with status 1, making it a quick end-to-end smoke test of the feature. You can also select specific files directly:

atmos ci validate .github/workflows/plan.yml .github/workflows/apply.yml

--workflow-path and explicit workflow-file arguments are intentionally separate selectors and cannot be combined. This keeps the validation scope unambiguous.

Put It in GitHub Actions

Add a validation step after checkout:

.github/workflows/validate.yml
name: Validate workflows

on:
pull_request:

jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: cloudposse/github-action-setup-atmos@v2
- run: atmos ci validate

When native CI is enabled, Atmos turns findings into line-anchored GitHub Actions annotations:

atmos.yaml
ci:
enabled: true

Annotations are enabled by default once ci.enabled is true; set ci.annotations.enabled: false to suppress them. Outside GitHub Actions, the same command has no provider side effects and simply renders its diagnostics.

SARIF Is Explicit

For a separate Code Scanning upload or an artifact, request SARIF explicitly:

atmos ci validate --format=sarif > actionlint.sarif

Atmos does not upload SARIF automatically. That avoids duplicate pull-request feedback and keeps security-events: write unnecessary unless your workflow deliberately adds a SARIF upload step.

The validator respects .github/actionlint.yaml and .github/actionlint.yml. Its optional ShellCheck and Pyflakes integrations are disabled for now, so the command stays deterministic and does not depend on tools installed on the runner.

Learn More