A deploy that races itself does not fail. It succeeds — twice, in the wrong order — and what is left behind is a green check on the newer commit sitting above a live site running the older one. Paste a GitHub Actions workflow below and this page names every way yours is permitted to do that, with the line and the fix. Free, no sign-in, and nothing you paste leaves your device.
Paste one workflow at a time. Nothing is uploaded — the scan runs in this page.
Nothing scanned yet. Paste a workflow above, or load the example to see what a report looks like.
github.sha, github.run_id and friends. Every
run gets its own group, so nothing waits for anything. The dangerous one,
because the block is there and reads as protection.cancel-in-progress on a deploy. Right for
tests, wrong for a deploy: a cancelled deploy has already written part of
its change and nothing re-runs to finish it.Read the longer version, with what each failure looks like from the outside, in how a deploy ships an older commit. There is also a comparison against reading the workflows yourself.
The same logic ships as check.py — Python standard
library only, no dependencies, no network access, and no GitHub token. A
test in this repository runs both over the same workflows and asserts they
report the same findings, so the CLI and this page cannot drift apart.
python3 check.py .github/workflows/ # scan them all
python3 check.py .github/workflows/ --fail-on-risk # exit 1 on a high-risk finding
python3 check.py deploy.yml --json # stable ids, for your own tooling
Two runs of the same deploy workflow overlapping in time. Push commit A, then commit B a minute later, and both runs start. If A's run happens to finish last — because it queued for a runner, because its build was slower, because a step retried — then A is what is live, while B's green check sits at the top of the branch. Nothing failed, and the run log for A does not say it overwrote anything, because from inside that run nothing did.
Only if the group name is the same for every run that writes to the same target. A group keyed on something that changes each run puts every run in a group of its own, so nothing ever waits for anything. That is the same race as having no block at all, with the difference that the block is there and reads as protection, which is why it survives review. Two workflows deploying the same environment under two different group names have the same problem for the same reason.
Usually not. Cancelling is right for tests, where the only cost of stopping a superseded run is the time it had used. A deploy cancelled part-way through has already written part of its change — some of a batch of uploaded files, half a copied artifact, a migration that ran without the code expecting it — and nothing re-runs to finish it. For a deploy, queueing the newer run instead means it applies last, which is the order you want.
No. The scan runs in this page's own JavaScript, on your device. Nothing you paste is sent anywhere, stored on a server, or logged. This page makes no network request of any kind after it loads — no analytics service, no font, no CDN. The same logic ships as a stdlib-only Python script with no network access at all, if you would rather not paste a workflow into a web page.
Yes. The CLI takes workflow files or a directory of them, prints the same findings this page shows, and can exit non-zero when a high-risk finding is present, so it can gate a pull request. It is free, MIT-licensed, has no dependencies beyond the Python standard library, and never calls the GitHub API.