GitHub Actions workflow explainer
Paste a GitHub Actions workflow: the job graph with a what-if-this-fails simulator, every matrix job after include/exclude, triggers with a branch/tag/path filter tester and next cron runs, a ${{ }} expression evaluator with an editable context, GITHUB_TOKEN permissions per job, concurrency groups, and actionlint's checks plus security checks.
Added
How to use it
I read a lot of other people’s workflows, and the questions are always the same: which jobs does
this matrix make, will a push to this branch run it, why was that job skipped, and is the
pull_request_target in here safe. This page answers them from the YAML.
How to use it
Paste a workflow (.github/workflows/.yml), open a file, or pick an example: Go, Rust,
Java with Gradle, a Node matrix, a reusable workflow with a composite action, an unsafe pull
request helper, and a deliberately broken one. If the workflow uses local actions
(uses: ./.github/actions/x) or calls a reusable workflow (uses: ./.github/workflows/y.yml),
add those files under *Other files with their paths and they are checked too.
-
Lints — what actionlint reports, apart from its shellcheck and pyflakes passes: unknown keys,
${{ }}syntax and functions, contexts used where GitHub doesn’t allow them,matrix.,steps.,needs.andinputs.references that don’t exist,needscycles, event names and activity types, cron syntax and the five-minute minimum, runner labels, inputs of popular actions and of your local actions and reusable workflows, permission scopes, deprecated::set-output, and anif:that is always true because of text around${{ }}. Then the security checks: script injection from untrustedgithub.eventfields inrun:andactions/github-script,pull_request_targetorworkflow_runchecking out the pull request’s code, third-party actions pinned to a tag or branch (with thegh apicommand that finds the commit SHA to pin to), missing or broadpermissions,secrets: inheritand self-hosted runners on pull requests. Click a position to jump to the line. -
Jobs & matrix — the
needsgraph. Click a job to make it fail (or be cancelled) and see which jobs still run, withif:evaluated againstneeds.<job>.resultand the implicitsuccess() &&. Each job lists every matrix combination with its display name, which ones came frominclude, whichexcluderemoved, and whatfail-fastandmax-paralleldo. -
Triggers — each event explained, activity types (including the defaults for pull requests), filters, the next runs of each
schedule(in itstimezone, linked to the cron tool), theworkflow_dispatchform with a matchinggh workflow runcommand, and Would it run?: give an event, a ref and the changed files and it says yes or no, and why. -
Expressions — evaluate any expression against a context built from the workflow (github and a sample event payload, env, vars, secrets, inputs, matrix, needs, steps, runner, job, strategy) that you can edit, and see every
${{ }}in the file evaluated with it. Secrets are masked as*. -
Permissions — the effective
GITHUB_TOKENscopes for each job, given the repository’s default setting, the event, and whether the pull request comes from a fork. Then each concurrency group with its value for the chosen context.
How it matches GitHub
The expression evaluator follows GitHub’s documented rules: strings compare without regard to
case, values of different types are compared as numbers ('' == 0, null == 0, '1' == 1),
NaN equals nothing, arrays and objects are equal only to themselves, && and || return one
of their operands (so cond && 'a' || 'b' works as a ternary), and .* filters arrays. The
matrix expansion follows the documented include algorithm: an include entry is added to every
original combination it doesn’t contradict, and becomes a new combination when it fits none.
I compared the lints with actionlint 1.7.12 on 400 real workflow files: it reports 364 problems
and this page finds 357 of them on the same line. The other seven need actionlint’s type
inference (comparing an object to a string) or a self-hosted label list. The YAML parser gave
the same values as js-yaml on all of them. Things this page can’t know: whether an action or
a tag exists, a repository’s own settings, and the contents of files that hashFiles() would
hash — it returns a made-up hash.
The runner labels, webhook activity types and the inputs of popular actions come from actionlint’s data (MIT licence).
Privacy
Everything runs in this page. The workflow and files you paste are kept in this browser’s local storage so they are still there next time; nothing is uploaded. Share link puts the workflow in the link itself.
Related: Compose and Kubernetes checker, cron tool,
bash explainer for the run: scripts, Makefile
explainer, git helper.
Feedback is a gift. Something to improve, a feature to add, or a bug? Tell me.
Runs entirely in your browser; nothing you type leaves this page.