Skip to content

Release runs one CI pass, and the tag it cuts agrees with its own lockfile - #64

Merged
leeovery merged 3 commits into
mainfrom
fix-release-ci
Aug 26, 2026
Merged

Release runs one CI pass, and the tag it cuts agrees with its own lockfile#64
leeovery merged 3 commits into
mainfrom
fix-release-ci

Conversation

@leeovery

Copy link
Copy Markdown
Owner

The v0.1.1 release broke CI twice over — one structural, one real.

Two runs per release

on: push with no filter fires for tag pushes too, so the release produced one run for the branch push and one for the tag — the same tree twice, right after the release preflight had already run the identical four gates locally. CI now triggers on branch pushes only (branches: ["**"]); PRs unchanged.

Both runs failed — the lock drift

Every job died on uv sync --locked: mint's version bump landed version = "0.1.1" in pyproject while uv.lock still recorded 0.1.0 (the lock records the project's own version). The preflight couldn't have caught it — it ran uv sync --locked on a tree that was still consistent, and the bump that broke it came after.

Where the fix can live was established empirically, not by reading docs (probe repo, three releases driven through real mint release runs):

  • mint's pre_tag hook runs on the pre-bump tree — releasing v0.0.3, a probe hook read the version file and saw 0.0.2 — so a naive uv lock there locks the old version;
  • no hook runs between mint's bump-commit and the tag;
  • but mint exports MINT_NEW_VERSION to hooks, and sweeps any tree changes a hook makes into a chore(release): pre-tag artifacts commit before tagging.

So the hook now does the bump itself: uv version "$MINT_NEW_VERSION" moves pyproject and uv.lock together, mint's artifacts commit carries both, and mint's own bump then finds the file already current — the bookkeeping commit carries just the changelog. Verified end-to-end in the probe repo: a checkout of the resulting tag passes uv sync --locked.

This also fixes a second latent defect from the same ordering: uv build was building the wheel from the pre-bump tree, so v0.1.1's sanity-check wheel was actually versioned 0.1.0.

What this PR does not do

The v0.1.1 tag keeps its inconsistent lock — moving a published tag is worse than the blemish, and instances never read the lock (the shim's uvx builds from the tree). Main's drift is re-locked here, so CI on main goes green on merge.

CLAUDE.md's CI and release-gate paragraphs updated to stay true.

Four gates green locally, including the exact uv sync --locked that failed in CI: 2041 passed, ruff, ty, format.

leeovery and others added 3 commits August 26, 2026 19:18
…kfile

The v0.1.1 release broke CI twice over. The tag push re-ran the suite the
release preflight had just run on the identical tree — `on: push` with no
filter fires for tags too — so one release produced two runs. And both
runs failed honestly: mint's version bump landed a pyproject that
disagreed with uv.lock, because the lock records the project's own
version and nothing re-locked it. The preflight could not have caught
this — it ran `uv sync --locked` on a tree that was still consistent, and
the bump that broke it came after.

CI now triggers on branch pushes only; a tag never re-runs what preflight
already proved on the same tree.

The bump fix lives where the drift is made. mint's pre_tag hook runs on
the PRE-BUMP tree (device-verified with a probe repo: releasing v0.0.3,
the hook read the version file and saw 0.0.2) and no hook runs between
mint's bump and the tag — so the hook now does the bump itself:
`uv version "$MINT_NEW_VERSION"` moves pyproject and uv.lock together,
mint sweeps both into its pre-tag artifacts commit, and its own bump
finds the file already current. This also fixes a second latent defect
the same ordering caused: `uv build` was baking the pre-bump version into
the wheel, so v0.1.1's sanity-check wheel was versioned 0.1.0.

The stale lock on main is re-locked here. The v0.1.1 tag itself keeps its
inconsistent lock — moving a published tag is worse, and instances never
read it: the shim's uvx builds from the tree.

Verified end-to-end in a scratch repo: with the hook in place the tag's
checkout passes `uv sync --locked`, and the bookkeeping commit carries
just the changelog.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The config says what it does; the why lives in CLAUDE.md, which is the
present tense. Drops the ten-line pre_tag block from .mint.toml and the
branch-filter block from ci.yml, trues up ci.yml's header (it still
claimed every push), and re-wraps the CLAUDE.md prose to the file's width.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Same reason as the last commit: .mint.toml is now fifteen lines that say
exactly what they do, and CLAUDE.md's release-gate paragraph carries every
why the comment was holding.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@leeovery
leeovery merged commit 0683dfd into main Aug 26, 2026
@leeovery
leeovery deleted the fix-release-ci branch August 26, 2026 22:10
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