Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
fc77ef8
1.4.0: the month ubuntu-latest is two operating systems
Booyaka101 Sep 20, 2026
0b11e4a
guard --no-update-lock now writes nothing on the first run either
Booyaka101 Sep 20, 2026
6c5c98a
Attribute ImageOS to the job that asked for the label
Booyaka101 Sep 20, 2026
e503430
A pinned job is not the floating label running late
Booyaka101 Sep 20, 2026
dd5f9c8
Scope the matrix label scan to matrix blocks
Booyaka101 Sep 20, 2026
66c6495
A flow-mapping matrix is a matrix too
Booyaka101 Sep 20, 2026
777176d
An expression runs-on reads the whole file again, minus the prose
Booyaka101 Sep 20, 2026
17dcddc
Keep the job rule, the lock's tools and trailing comments straight
Booyaka101 Sep 20, 2026
51fcaf7
One attribution rule, read from one place
Booyaka101 Sep 20, 2026
52450bd
Keep the two new line scanners linear, and let a step keep its keys
Booyaka101 Sep 20, 2026
7ecf9c8
The step summary names the cause too, and one manifest diff serves bo…
Booyaka101 Sep 20, 2026
643405f
stripComment searches for the comment instead of matching to the anchor
Booyaka101 Sep 20, 2026
b8dc15e
Three small consistency gaps around the migration lane
Booyaka101 Sep 20, 2026
a5f0209
The mid-window sentence has to read right in plan too
Booyaka101 Sep 20, 2026
fc40382
Make the lock-schema guard test deterministic and offline
Booyaka101 Sep 20, 2026
c04bde6
One walk per file, one manifest read per label, and --json on every path
Booyaka101 Sep 20, 2026
a9192e0
The job id decides, and a pinned sibling outranks a coincidence
Booyaka101 Sep 20, 2026
c67f5f9
Read the file as YAML at the edges: folded prose, the end of jobs, a …
Booyaka101 Sep 20, 2026
a42f466
A slipped rollout is not your build's fault
Booyaka101 Sep 20, 2026
6abcb0d
Do not remember a manifest read that failed
Booyaka101 Sep 20, 2026
ae587fe
Scope the drift explanation the way attribution is scoped
Booyaka101 Sep 20, 2026
788b7b4
One place to read a table keyed by a string we did not choose
Booyaka101 Sep 20, 2026
7d8b89f
A job id is unique in a file, not in a repo
Booyaka101 Sep 20, 2026
0c71c4e
Finish the own-key sweep: five tables were still read as prototypes
Booyaka101 Sep 20, 2026
089a819
Tool names reach the diff from a lock file, so the maps need no proto…
Booyaka101 Sep 20, 2026
4c0f07d
A lock from the other side of a migration is not this image's history
Booyaka101 Sep 20, 2026
b3a07be
A job id alone is unique in a file, so it cannot outvote another file
Booyaka101 Sep 20, 2026
805aebb
A rate limit is not a reason to report every tool as removed
Booyaka101 Sep 20, 2026
9ad005b
Place a runs-on: an expression resolves outside the jobs map, and cou…
Booyaka101 Sep 20, 2026
9a18dcd
Read a map under an empty prose key, and badge the migration outcome …
Booyaka101 Sep 20, 2026
278e135
Do not call a job's own matrix leg a second workflow with the same jo…
Booyaka101 Sep 20, 2026
1edcb5f
One site per label line, read matrix axes named like prose keys, and …
Booyaka101 Sep 20, 2026
8feeb3a
Keep the matrix token scan out of jobs that name their runner outright
Booyaka101 Sep 20, 2026
bfd437c
A uses: job keeps the label it passes on, and a namesake is described…
Booyaka101 Sep 20, 2026
a7a4ada
A job id that runs somewhere else too cannot speak for this label
Booyaka101 Sep 20, 2026
28f22cf
Let the label gates report and decide when there are no tools to watch
Booyaka101 Sep 20, 2026
d75033d
A tool the manifest could not be read for is unobserved, not removed
Booyaka101 Sep 20, 2026
6322308
Say which manifests really omit which header field
Booyaka101 Sep 20, 2026
ebb7934
An unlisted tool with no probe recipe is not a removal either
Booyaka101 Sep 20, 2026
fd16bc6
Say days in the brownout countdown, and drop a flag nothing reads
Booyaka101 Sep 20, 2026
83ac232
A run-name or an input description is prose, not a runner
Booyaka101 Sep 20, 2026
118abf3
Only strategy.matrix is a matrix, and one missing directory is one no…
Booyaka101 Sep 20, 2026
a6f7386
Refresh the headline plan example, which predates the image-header rows
Booyaka101 Sep 20, 2026
bb0c531
Derive the locked Node version instead of pinning one CI ships
Booyaka101 Sep 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,38 @@
env:
GITHUB_TOKEN: ${{ github.token }}

migration:
# Deliberately floating. Every other job here pins its image; this one is the
# lane's subject, and reading ImageOS off a runner that GitHub is in the
# middle of re-pointing is the half no test can reach.
runs-on: ubuntu-latest

Check notice on line 141 in .github/workflows/ci.yml

View workflow job for this annotation

GitHub Actions / migration

runner-drift: ubuntu-latest becomes ubuntu-26.04 in 29 days

ubuntu-latest moves from ubuntu-24.04 to ubuntu-26.04. The rollout starts 2026-10-19 (29 days) and finishes 2026-11-19 (60 days). See https://github.com/actions/runner-images/issues/14748
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: '22'

- name: plan the announced ubuntu-latest move against the live manifests
run: node src/cli.mjs plan --from ubuntu-latest
env:
GITHUB_TOKEN: ${{ github.token }}

# The exit code is deliberately not asserted: it is 1 while the window is
# ahead of this runner and 0 once the move has reached it, so pinning it
# would turn the rollout itself into a red build. What must hold on every
# run is that the lane resolved the label and named its source.
- name: guard --fail-on-migration on a real floating runner
run: |
set +e
out="$(node src/cli.mjs guard --tools node --fail-on-migration 30 --no-update-lock 2>&1)"
code=$?
printf '%s\n' "$out"
echo "exit $code"
grep -q 'ubuntu-latest' <<<"$out" || { echo 'the migration lane said nothing'; exit 1; }
grep -q 'actions/runner-images#14748' <<<"$out" || { echo 'no source named'; exit 1; }
env:
GITHUB_TOKEN: ${{ github.token }}

runners:
runs-on: ubuntu-24.04
steps:
Expand Down
375 changes: 375 additions & 0 deletions CHANGELOG.md

Large diffs are not rendered by default.

223 changes: 201 additions & 22 deletions README.md

Large diffs are not rendered by default.

8 changes: 7 additions & 1 deletion action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ inputs:
description: 'Fail if a pinned runs-on label browns out or retires within this many days. Empty disables the check.'
required: false
default: ''
fail-on-migration:
description: 'Report, and fail on, an announced migration of a floating runs-on label (ubuntu-latest) starting within this many days. Empty disables the check.'
required: false
default: ''
fail-on-deprecation:
description: 'Fail if this self-hosted runner version loses support within this many days. Needs a token with administration read; empty disables.'
required: false
Expand All @@ -39,7 +43,7 @@ inputs:
version:
description: 'npm version of runner-drift to run.'
required: false
default: '1.3.0'
default: '1.4.0'
package:
description: 'Override the npm spec, e.g. a local .tgz built in the same job. Mainly for testing this action before the version it requests exists on npm.'
required: false
Expand Down Expand Up @@ -77,6 +81,7 @@ runs:
RD_FAIL_ON_UNKNOWN: ${{ inputs.fail-on-unknown }}
RD_FAIL_ON: ${{ inputs.fail-on }}
RD_FAIL_ON_RETIREMENT: ${{ inputs.fail-on-retirement }}
RD_FAIL_ON_MIGRATION: ${{ inputs.fail-on-migration }}
RD_FAIL_ON_DEPRECATION: ${{ inputs.fail-on-deprecation }}
RD_TOOLS: ${{ inputs.tools }}
RD_LOCK_FILE: ${{ inputs.lock-file }}
Expand All @@ -98,6 +103,7 @@ runs:
args=(guard --lock-file "$LOCK_ABS" --workflows "$WF_ABS")
if [ -n "$RD_FAIL_ON" ]; then args+=(--fail-on "$RD_FAIL_ON"); fi
if [ -n "$RD_FAIL_ON_RETIREMENT" ]; then args+=(--fail-on-retirement "$RD_FAIL_ON_RETIREMENT"); fi
if [ -n "$RD_FAIL_ON_MIGRATION" ]; then args+=(--fail-on-migration "$RD_FAIL_ON_MIGRATION"); fi
if [ -n "$RD_FAIL_ON_DEPRECATION" ]; then args+=(--fail-on-deprecation "$RD_FAIL_ON_DEPRECATION"); fi
if [ -n "$RD_TOOLS" ]; then args+=(--tools "$RD_TOOLS"); fi
printf 'lock-file=%s\n' "$RD_LOCK_FILE" >> "$GITHUB_OUTPUT"
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "runner-drift",
"version": "1.3.0",
"version": "1.4.0",
"description": "Detect and attribute GitHub Actions runner-image tool drift: lock the tool versions your CI actually uses, diff them on every image bump, and plan a runner label migration before the deprecation deadline.",
"type": "module",
"bin": {
Expand Down Expand Up @@ -32,6 +32,8 @@
"runner-images",
"deprecation",
"ubuntu-22.04",
"ubuntu-latest",
"ubuntu-26.04",
"drift",
"migration",
"actions"
Expand Down
4 changes: 2 additions & 2 deletions runner-lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"schemaVersion": 1,
"label": "ubuntu-24.04",
"imageOS": "ubuntu24",
"imageVersion": "20260720.247.2",
"imageVersion": "20260907.300.1",
"tools": {
"Node.js": {
"versions": [
Expand All @@ -12,5 +12,5 @@
"command": "node --version"
}
},
"updatedAt": "2026-08-05T05:13:56.107Z"
"updatedAt": "2026-09-20T11:17:35.354Z"
}
Loading
Loading