Skip to content

CI: pin floating toolchain versions (#1645) - #1647

Open
Mikola Lysenko (mikolalysenko) wants to merge 4 commits into
mainfrom
agent/fix-ci-pin-toolchain-versions
Open

Mikola Lysenko (mikolalysenko) wants to merge 4 commits into
mainfrom
agent/fix-ci-pin-toolchain-versions

Conversation

@mikolalysenko

@mikolalysenko Mikola Lysenko (mikolalysenko) commented Oct 20, 2026 •

Copy link
Copy Markdown
Collaborator

LLM Description written by Claude Code:claude-opus-5-5

Fixes #1645

Problem

ci.yml installed several oracle toolchains at floating versions: node-version: '24.x' (e2e pnpm legs, yarn-classic matrix, hosted-e2e), python-version: '3.12.x' (e2e Python legs, hosted-e2e) and go-version: '1.24' (test, coverage, hosted-e2e). Any upstream release could then change what CI runs without a commit here. That is how uv 0.13.2 turned main red on 10-20 and stalled the merge queue for 2 h 44 min (about 4.3k job-min wasted; see #1645). pin-check.yml only enforced SHA pins on uses:, so nothing caught it.

Root cause

Nothing required tool versions to be exact. The uv pin itself already landed on main via #1605 (version: "0.13.1"). This PR pins the remaining runtimes and adds a lint so the next floating version is caught in review instead of in the merge queue.

Changes

  1. Exact pins in ci.yml, each with a "bump deliberately (CI perf: floating toolchain versions in ci.yml e2e — upstream uv 0.13.2 red-mained the merge queue for 2.7 h (~4.3k job-min wasted on 10-20; ~0.45k min/day amortized) #1645)" comment:
    • Node 24.x → 24.21.0. The vlt legs already use it; it was released 2026-09-07.
    • Go 1.24 → 1.24.13, the last 1.24 patch.
    • Python 3.12.x → 3.12.10. 3.12.11–3.12.15 are source-only security releases, and actions/python-versions builds them for Linux only. On the Windows and macOS e2e legs, 3.12.x already resolved to 3.12.10, so pinning a newer patch would break those legs. Linux Python legs move from the latest 3.12 patch to 3.12.10. They use Python only to host uv, poetry, pdm and hatch.
  2. New scripts/check-tool-pins.py, standard library only. It scans a workflow as text, the same way the other scripts/tests modules read ci.yml. It fails when:
    • astral-sh/setup-uv (matched case-insensitively) has no version:;
    • a setup-uv version, or any node-version / python-version / go-version input, is not an exact X.Y.Z (.x, bare major or major.minor, latest, ranges);
    • an expression reads anything but matrix.* (for example vars.*, env.* or inputs.*), yields a non-exact quoted value (comparison operands such as != '' are skipped), or reads a matrix.<key> with a non-exact value in that job's matrix. A fromJSON matrix can't be resolved statically and is left to review.
  3. The gate is scripts/tests/test_check_tool_pins.py. The lean lint-ecosystems job already runs python3 -B -m unittest discover -s scripts/tests on every PR, so Repository.test_ci_yml_has_no_floating_toolchains blocks a floating version in ci.yml with no new job or step. It covers ci.yml only, the one workflow that gates PRs and the merge queue. The disabled compatibility workflows are untouched; the script flags their floating python-version: '3.12' if run on them by hand. pin-check.yml is unchanged.

Out of scope, same as the issue: php-version: '8.2' (setup-php only takes major.minor), java-version majors, dotnet-version: N.0.x, npm install -g pnpm@10 in the e2e pnpm legs, and the package managers hosted-e2e installs by major (pnpm@10, bun@1, unpinned uv). hosted-e2e tracks current releases on purpose and runs only in the full tier. Optional item 4 in the issue (a fail-fast "base is red" summary) is not done here.

Runner usage (lean PR run)

  • Before: about 28 Linux jobs, about 8 min. After: the same jobs and tiers. No job is added, removed or moved. lint-ecosystems runs 21 more unit tests, which take about 20 ms.
  • Linux legs that pin Python may download 3.12.10 if the runner's tool cache holds only a newer patch. That adds a few seconds per leg.
  • Merge queue: unchanged. The ci-ok and clippy names and the ci-ok aggregation are unchanged.

Test evidence

  • python3 scripts/check-tool-pins.py on origin/main's ci.yml: exit 1, flagging exactly the 8 floating lines (403, 620, 1422, 1428, 2241, 2637, 2682, 2702). On this branch: exit 0.
  • Red→green: with origin/main's ci.yml copied in, test_ci_yml_has_no_floating_toolchains and test_main_exit_codes FAIL. With this branch's ci.yml, all 21 tests pass.
  • python3 -B -m unittest discover -s scripts/tests (what lint-ecosystems runs): 368 tests OK, 6 skipped. Neither file imports anything outside the standard library.
  • actionlint 1.7.7 on ci.yml: the same findings as main, none new. zizmor --offline: the same 61 findings as main, none new.
  • No fromJSON matrix changed, and no Rust code changed.
  • CI on 4c5f403: lean PR run green (87 checks, lint-ecosystems ran the new gate). One full-scope dispatch, run 39499480887: success, 246 jobs, including the Windows and macOS e2e legs and hosted-e2e on the new Node, Python and Go pins.
  • Bugbot on 4c5f403: no issues.

/code-review high

Fixed: the PyYAML dependency (the lint is now stdlib only), the duplicate pin-check step (dropped), the unchecked vars/env/inputs expressions, comparison operands flagged as versions, and the case-sensitive setup-uv match.
Not fixed, with reasons:

Per-issue checklist:


Generated by Claude Code

Assisted-by: Claude Code:claude-opus-5-5
The e2e, yarn-classic, test, coverage and hosted-e2e jobs installed
Node '24.x', Python '3.12.x' and Go '1.24'. Any upstream release then
changed what CI ran with no commit here, which is how uv 0.13.2 turned
main red and stalled the merge queue for 2.7 h on 10-20.

Pin them to exact releases: Node 24.21.0 (already used by the vlt
legs), Go 1.24.13, and Python 3.12.10, the newest 3.12 that
actions/python-versions builds for Windows and macOS as well as Linux.
Later 3.12 patches are source-only, so '3.12.x' already resolved to
3.12.10 on those runners.

Refs #1645

Assisted-by: Claude Code:claude-opus-5-5
pin-check only made `uses:` references immutable. Nothing stopped a
setup step from taking the latest tool, so a new upstream release
could break the merge queue until someone landed a pin.

Add scripts/check-tool-pins.py and run it from pin-check on ci.yml,
the only workflow that gates PRs and the merge queue. It fails when
setup-uv has no `version:`, or when a uv, node, python or go version
is not an exact X.Y.Z, including literal fallbacks and the matrix
values an expression reads. The pin-check job moves to the Depot
runner mapping with the DISABLE_DEPOT_RUNNERS switch.

Fixes #1645

Assisted-by: Claude Code:claude-opus-5-5
Review follow-up. The pin lint imported PyYAML, which the runner's
python3 may not have; on such a runner the new test module would break
the required lint-ecosystems job. The lint now scans the workflow as
text with the standard library, like the other ci.yml tests do.

The pin-check step is dropped. lint-ecosystems already runs every
scripts/tests module on each PR, so the repository test gates ci.yml
with no new step or job, and pin-check is back to its main version.

The lint also now rejects expressions that read vars/env/inputs, skips
comparison operands such as `!= ''`, and matches setup-uv regardless
of case.

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko
Mikola Lysenko (mikolalysenko) marked this pull request as ready for review October 20, 2026 21:03
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 4c5f403. Configure here.

@mikolalysenko Mikola Lysenko (mikolalysenko) added the Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review label Oct 20, 2026
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

Burn-down agent: labeled Ready for review at 4c5f403.

  • CI: every check run read on this head is green (277 success, 23 skipped, 0 failed). That includes the lean PR run and full-scope dispatch run 39499480887. No main-wide failures.
  • Bugbot: reviewed 4c5f403, no findings.
  • Mergeable: no conflicts with main (dc0dc75), and no CHANGELOG.md change.
  • Reviewer focus: whether the Python 3.12.10 pin is right. The PR description explains that newer 3.12 patches are source-only on Windows and macOS.

Slack announcement not sent: this run has no Slack send tool.


Generated by Claude Code

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

Labeled Ready for review (burn-down agent).

  • Head: 4c5f403. Mergeable, no conflicts; CHANGELOG.md not touched.
  • CI: 333/333 check runs green on the head (lean CI plus the full-scope run). No main-wide failures.
  • Bugbot: reviewed 4c5f403, no new issues; 0 open review threads.
  • Reviewer focus: the pinned versions in .github/workflows/ci.yml and the new scripts/check-tool-pins.py guard that keeps them pinned.

Generated by Claude Code

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

Final review brief (final reviewer agent)

What it does. Pins the Node (24.x → 24.21.0), Python (3.12.x → 3.12.10) and Go (1.24 → 1.24.13) toolchains in ci.yml to exact releases, so an upstream release can't change CI without a commit (the uv 0.13.2 queue stall, #1645). It adds a stdlib-only lint, scripts/check-tool-pins.py, which the existing lint-ecosystems unittest run uses to fail any new floating node/python/go/setup-uv version in ci.yml.

Risk: low. CI-only; no Rust or shipped code changes. The one behavior change is that Linux Python legs drop from the latest 3.12 patch to 3.12.10, the last patch with Windows and macOS builds. Those legs only use Python to host uv, poetry, pdm and hatch. A full-tier dispatch ran on the new pins and passed.

Look here

Verified

  • I read the full diff. Locally on 4c5f403: check-tool-pins.py ci.yml exits 0. On origin/main's ci.yml it exits 1 and flags the same 8 floating lines. python3 -B -m unittest discover -s scripts/tests: 368 OK, 6 skipped.
  • The only remaining node-version values in ci.yml are exact (20.20.2, 24.21.0), and the setup-uv steps all set a version.
  • CHANGELOG.md is untouched. The branch is up to date with main and mergeable. No unresolved threads.
  • CI: all 333 check runs on the head are green or skipped, including ci-ok and clippy. Bugbot found no issues.

Changes I made: none.

Open questions: none blocking. The PR description lists what's out of scope: php/java/dotnet majors and package-manager versions in hosted-e2e.

Auto-merge is armed, so approving sends this straight to the merge queue.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci-perf CI / merge-queue performance finding (profiler routine) Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review

Projects

None yet

2 participants