Skip to content

Latest commit

 

History

7 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ci-eol

CI OpenSSF Scorecard CodeQL

What in your CI is already dead, and what dies on a published date.

py -m ci_eol.cli .

Exit 0 when nothing inside the window is dying, 1 when something is, 2 when the question could not be asked. Python standard library only, no dependencies, no API key, no network call — the tables ship with the tool.

Why this exists

A workflow keeps running right up to the day it does not. The runner image is retired, the language version stops getting security fixes, the action's major version is switched off — each on a date that was published months earlier, and none of which produces a warning in your repository first.

The scale is not a guess. GitHub code search, 21 September 2026:

What a workflow still names Files on GitHub
actions/checkout@v3 1,335,296
node-version: 18 (EOL 30 April 2025) 516,096
ubuntu-22.04 (left support 17 September 2026) 230,400
python-version: "3.9" (EOL 31 October 2025) 165,632
actions/upload-artifact@v3 (deprecated 30 November 2024) 130,048
ubuntu-20.04 (retired 15 April 2025) 117,504

Dependabot raises a pull request when a version moves. It does not tell you that the thing you have stops working on the eleventh of a month. That is a different question, and it is the one someone asks the morning their build goes red: what of mine is dying, when exactly, and what replaces it.

A real run

Against my own repositories on 21 September 2026 — which is the only honest way to show a tool like this:

$ py -m ci_eol.cli ../repo-secret-scanner
ci-eol - tables copied 2026-09-21
  runner images   https://endoflife.date/github-actions-runner-images
  language EOL    https://endoflife.date
  action notices  https://github.blog/changelog/2024-04-16-deprecation-notice-v3-of-the-artifact-actions/

2026-10-31  in 40 days
    language version Python 3.10   1 occurrence(s)
        upstream support runs to 2026-10-31
        .github/workflows/ci.yml:18  python-version: ["3.10", "3.12"]

1 occurrence(s) are past their date or die within 90 days.

Exit code 1. Across 31 repositories it found Python 3.10 in 17 of them, forty days from the end of upstream support, and Node.js 20 in one, already 144 days past it. Including this repository's own CI, which is the point: a check its author exempts himself from is advice, not a check.

The part that is not a grep

The identifiers overlap, and a report with a false alarm in it is read once and then ignored:

  • ubuntu-22.04 is a prefix of ubuntu-22.04-arm64, and they retire on different terms. Matches are bounded on both sides and ordered longest-first.
  • @v3 is a prefix of @v3.1.0. A full tag is read by its major.
  • A SHA pin says nothing about the major, so it is not guessed at.
  • ${{ matrix.python-version }} carries no version; the numbers live where the matrix is declared, and python-version: ["3.10", "3.12"] yields both.

The test suite asserts that the current major of every action in the table is never reported, and that every live runner image stays out of the report.

Being behind is not being switched off

Two different facts, kept apart:

  • A published date. actions/upload-artifact@v3 was deprecated on 30 November 2024, ubuntu-20.04 retired on 15 April 2025. These carry a date and they set the exit code.
  • Majors behind. actions/checkout@v3 is four majors behind v7. That is worth knowing and it is not a death sentence, so it is printed in its own section and never fails a build.

Usage

py -m ci_eol.cli .                       # this repository
py -m ci_eol.cli .github/workflows/ci.yml  # one file
py -m ci_eol.cli . --within 30           # fail only on the next 30 days
py -m ci_eol.cli . --json report.json    # machine-readable
py -m ci_eol.cli . --exclude '.github/workflows/experimental.yml'
py -m ci_eol.cli --list                  # print the tables
py -m ci_eol.cli --check-source          # ask the feeds whether the dates moved

The tables can check themselves

Every command above works offline, which is the point: a check that needs the network fails in an air-gapped build. --check-source is the exception and is opt-in for that reason.

$ py -m ci_eol.cli --check-source
ci-eol - comparing the tables copied 2026-09-21 against the feeds

github-actions-runner-images
  every date in the table is the date the feed gives, and the feed knows no dated cycle this table lacks
...
Action deprecations are not checked: theirs is a blog post rather than a feed.
The tables still match every feed.

Exit 0 when the feeds agree, 1 when a date moved or a dated cycle is missing, 2 when a feed could not be read. Because endoflife.date publishes JSON, the comparison is date against date — a moved date is reported with both values rather than guessed at from prose. Action deprecations are deliberately left out: theirs is a blog post, and a second parser for prose would be another thing to keep right.

Seven of the tests feed the checker by hand instead of over the network, because a run on the day the tables were copied proves only that it reports agreement. Two of those seven exist because the first version compared in one direction only: it asked whether the table agrees with the feed, never whether the table holds a row the feed has never heard of.

Honest limits

  • The tables are a dated snapshot, not a feed. Copied on 21 September 2026 from the three sources named in every report. Past sixty days the tool says out loud that they are old, and --check-source answers whether they have actually drifted. A silently stale table would be worse than no tool.
  • Only what the workflow names. A version installed by a shell script, a Dockerfile, or an action's own default is invisible here. "No findings" means nothing named in the file is dying, not that nothing is.
  • Action deprecations are only the ones GitHub dated. The artifact actions have a published notice; most actions never get one, so they appear under "majors behind" instead of being invented a date.
  • Upstream EOL is not your EOL. A language past upstream support still runs. What it stops getting is security fixes, which is a decision for you and not something this can make.
  • GitHub Actions only. GitLab CI, CircleCI and Jenkins name the same things in a different shape; the matcher would need their syntax, and adding it is adding a reader, not rewriting the tables.

Licence

MIT, © Dmytro Galko. See LICENSE.

About

What in your CI is already dead, and what dies on a published date: runner images, language versions, action majors. Dated tables, no network, exit 1 for CI.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages