Finds the lines in .github/workflows that are valid today and fail on a date GitHub has already published.
This page checks the one file you pasted. The full version scans every workflow in the repository at once, exports the dated audit as CSV/JSON/HTML, applies the replacement line for you, and emits JSON a CI step can fail on — so an expiring line cannot merge.
$29 once · one licence key per person or team seat · Upwork lists DevOps engineers at $40-$100/hour, median $60.
Free for the file open in your editor - no key, no limit. The workspace sweep and the report ask for a key.
npmDocker Hubnpx @readystack/workflow-expiry-check <file> docker run --rm -v "$PWD:/w" getreadystack/workflow-expiry-check /w
Team? One key for every ReadyStack linter, 5 seats, $149 once
This page is the working piece. The full pack has everything below.
Finds the lines in .github/workflows that are valid today and stop working on a date GitHub has already published.
Upwork lists DevOps engineers at $40-$100/hour, median $60.
Buy the full version — $29· ReadyStack
Real numbers from this tool, line by line.
There is a category of CI failure that no linter warns you about, because nothing is wrong yet.
On 2026-09-08, this is a completely valid workflow. actionlint passes it. The official GitHub Actions VS Code extension passes it. The schema is correct, the labels exist, the actions resolve.
name: ci
on: [push]
jobs:
build:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/upload-artifact@v4
Every line in it has a published expiry date.
2026-09-17 - ubuntu-22.04 and ubuntu-22.04-arm begin deprecation. Full removal is 2027-04-17, and GitHub runs brownout windows before that in which jobs on the image simply fail. Right now the label is valid, so a validity check has nothing to report.
2026-09-23 - Node 20 is removed from the runner. Runners have defaulted to Node 24 since 2026-06-16. Any action whose action.yml declares runs.using: 'node20' stops executing that day, and that includes actions/checkout@v4, actions/setup-node@v4 and v5, and the v4/v5 artifact actions. The ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION escape hatch expires on the same date.
A linter answers "is this valid?" A calendar answers "is this still valid in three weeks?" Those are different questions and the second one is the one that pages you at 3am.
actionlint does flag an action whose runner is *already* unsupported - node12 and node16 today. It validates runner labels against the list of labels that *currently* exist. Both checks are correct and both are blind to an announced future date, by design. The information you need is not in the file and not in the schema. It is in a changelog post from last year.
name: ci
on: [push]
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v6
- uses: actions/upload-artifact@v6
While you are in there, three more things carry dates or teeth:
::set-output, ::save-state, ::set-env and ::add-path were removed. They do not error.They are ignored, so the value downstream is silently empty. Use >> "$GITHUB_OUTPUT".
uses: some-vendor/action@main pins to a moving branch. Anyone who can force-push that branchruns code in your pipeline with your secrets. Pin the full 40-character commit SHA.
${{ github.event.issue.title }} inside a run: block is shell injection. The title is writtenby whoever opened the issue. Pass it through env: and quote "$VAR" in the script.
I built a VS Code extension for this, because the check belongs where the file is being edited rather than in a report someone reads quarterly. It carries 29 rules, each one carrying its date and its replacement line, and checking the file you have open is free - all 29 rules, no limit.
There is also a single-page version that runs entirely in the browser: paste a workflow, get the line numbers back. Same rule set, same answers, nothing uploaded.
The thing worth internalising is smaller than the tool: "it passes the linter" and "it will still run next month" are two different claims, and almost nothing in the ecosystem checks the second one.
It reads a GitHub Actions workflow file and flags the lines that are valid today but stop working on a date GitHub has already published. Each finding gives the line number, the reason, the date, and the replacement line. It covers 29 rules: retiring runner images, the Node 20 removal, shut-down action versions, deleted workflow commands, and script injection.
The engineer who owns .github/workflows for a team that ships to production, and anyone maintaining a repository with more workflows than they can read by hand. It is aimed at people who find out a pipeline broke when a deploy fails on a Monday morning, rather than when the deprecation notice was published a year earlier.
actionlint is excellent and checks whether a label is valid right now. That is a different question. On 2026-09-08 ubuntu-22.04 is still a perfectly valid label, so no validity check flags it, yet its deprecation starts 2026-09-17. Workflow Expiry checks the calendar, not the schema, and needs no Go binary installed.
Checking the workflow file you have open is free and complete: every one of the 29 rules, no watermark, no limit on how many times you run it. A licence unlocks a different job, not a bigger one: scanning every workflow in the repository at once, exporting the dated audit as a file, applying fixes, and emitting JSON for a CI gate.
Upwork lists DevOps engineers at $40 to $100 per hour, median $60. Reading forty workflow files against a deprecation calendar by hand is most of a working day, and it has to be redone every time GitHub publishes a new retirement date. The full version is $29 once.
One question, answered by the person who built it. Your email only if you want the answer sent.