Skip to content

Rehearse the release version refresh at PR time - #9445

Merged
MaxGhenis merged 8 commits into
mainfrom
max/release-lock-rehearsal-20260911
Sep 15, 2026
Merged

MaxGhenis merged 8 commits into
mainfrom
max/release-lock-rehearsal-20260911

Conversation

@MaxGhenis

Copy link
Copy Markdown
Contributor

Why

The first automatic version bump after #9428 failed on main in Update versioning at release_lock.py --refresh although PR CI had passed. --committed runs uv lock --check, which takes uv's satisfies fast path and accepts a lock written by another uv version; the bump forces a re-resolution and the runner's uv rewrote 37 resolution-marker lines. #9444 pinned uv 0.12.13 and committed the lock in that normalization. This closes the remaining gap the review of #9444 named: a contributor who regenerates the lock with any other uv still passes PR CI and breaks the next release.

What

  • .github/release_lock.py --rehearse: copies pyproject.toml and uv.lock into a temporary directory, bumps the copied root version by one patch level exactly as bump_version.py would, and runs the existing refresh (check_release_lock(root, refresh=True)) there. The copy holds only the two files; the project declares no dynamic metadata, so uv resolves it without the package tree. The checkout is never written; both files are re-read at the end to prove it.
  • pr.yaml ReleaseLock job: "Rehearse the release version refresh" after the committed check.
  • Tests in .github/tests/test_release_lock.py (mocked uv: passes when only the root version changes, fails with the guard's message when a marker is rewritten, checkout untouched either way; the RELEASE_LOCK_REAL_UV=1 probe covers the real resolver).
  • .github/release-lock.md: regenerate the lock with the pinned uv (uvx --from 'uv==0.12.13' uv lock); how --rehearse works; the stale "blocked until spm-calculator 1.0.0 is on PyPI" paragraph replaced. docs/spm.md: the calculator now resolves from PyPI.
  • Changelog fragment.

Verification

  • python -m unittest discover -s .github/tests -p test_release_lock.py: 33 tests OK; also OK with RELEASE_LOCK_REAL_UV=1 under uv 0.12.13.
  • --rehearse under uv 0.12.13 on this branch's lock: exit 0 ("Rehearsed the release refresh at version 1.825.3").
  • --rehearse on the pre-Pin the uv release toolchain and commit the lock in its marker normalization #9444 lock (2cda664): exit 2, "Versioning changed the reviewed dependency graph", while plain uv lock --check on that lock exits 0. That is the gap this closes.

🤖 Generated with Claude Code

MaxGhenis and others added 7 commits September 11, 2026 20:27
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`uv lock --check` takes uv's satisfies fast path, so it accepts a lock that a
different uv version wrote; the release bump re-resolves and can then rewrite
the reviewed graph, which only shows up on main. `--rehearse` copies
pyproject.toml and uv.lock into a temporary directory, applies the patch bump
bump_version.py would apply, and runs the existing refresh against the copy.
uv resolves that copy from project metadata alone, because the root declares
no dynamic metadata and needs no package tree.

The mode reuses check_release_lock, so it runs the same uv command, the same
validation and the same restore. It reads the checkout and asserts afterwards
that neither file changed, and reports the guard's own error with a pointer to
the release-lock document.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The ReleaseLock job checked the committed lock but never re-resolved it, so a
lock regenerated with an unpinned uv passed the pull request and failed the
next automatic bump on main. The rehearsal runs in the same job, after the
committed check, with the pinned uv the release job uses.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Tell contributors to regenerate the lock with the pinned uv rather than the uv
they happen to have, describe `--rehearse` and why it must run under that same
pin, and replace the paragraphs that still said the country lock was blocked
until spm-calculator 1.0.0 reached PyPI. The committed lock resolves that
release from PyPI with hashed artifacts.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…he documented uv

Review nits on the rehearsal: the two-file copy is only valid while the
project declares static metadata, so the project validator now rejects a
`dynamic` key; the command's failure path (exit 2 with the hint) has a test;
the documented rehearsal command now actually puts the pinned uv first on
PATH instead of printing its version beside an unpinned run; and the fixture
README no longer calls the production lock pending.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@MaxGhenis
MaxGhenis marked this pull request as ready for review September 12, 2026 01:23
Both conflicts were prose: the lock note and the SPM doc describe the
calculator requirement and the certified build's digest as main now
ships them, so main's wording is kept.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@MaxGhenis
MaxGhenis merged commit 2b3112e into main Sep 15, 2026
35 checks passed
@MaxGhenis
MaxGhenis deleted the max/release-lock-rehearsal-20260911 branch September 15, 2026 04:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant