refactor: derive the version from the git tag - #129
Merged
Merged
Conversation
[project].version was written in pyproject.toml and had to be kept in step with the tag by hand, via bump-my-version and (where the lock recorded it) a second entry for uv.lock. Every release therefore carried two or three copies of one number, and a way for them to disagree. hatch-vcs now derives the version from `git describe`, so the tag is the single source of truth: `dynamic = ["version"]` in [project], `source = "vcs"` under [tool.hatch.version], and no number in any file for a bump to rewrite. uv omits the version from the lock's own package block, so that copy is gone too. bump-my-version resolves the current version from the newest reachable tag instead -- the same answer hatch-vcs gives -- so it still proposes the right next number, it just no longer writes it anywhere. Two bump-my-version targets went with the written number, and neither is a loss: the [project] rewrite has nothing left to match, and uv omits the version from a dynamic project's own package block, so the lock can no longer disagree with the manifest after a bump -- which is what that entry existed to prevent. CITATION.cff keeps its entries: its version is a statement about a citation rather than a build input, so it has to be right in the commit the tag names. The pytest-rhiza pin moves 0.2.1 -> 0.6.0 in the same commit, because it has to. 0.2.1 predates a rhiza project deriving its version, and three of its assertions read the absent [project].version as '' and fail rather than skip -- the required-field check, the semver shape and the tag-agreement one -- turning the required rhiza-test job red. 0.6.0 is the first release that accepts a VCS-derived version, is what rhiza-task 1.7.0 already resolves by default, and is what jquantstats pins. Verified: the tree at this repo's newest tag builds to exactly that tag's version, and between tags to the next patch as a dev release. rhiza_release.yml (v1.8.0) already handles a derived version -- it skips the `uv version --short` comparison and checks the built distribution against the tag instead -- and its build job checks out with fetch-depth: 0, which hatch-vcs requires. pytest-rhiza's test_pyproject and the synced test_rhiza_packaging both skip their written-version assertions on a dynamic project. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
[project].versionis no longer written inpyproject.toml. hatch-vcs derives it fromthe git tag, so the tag is the single source of truth:
Why
The version was written in the manifest and kept in step with the tag by hand — through
bump-my-version, and in some repos a second entry for the copy
uv.lockrecorded. Thatis two or three copies of one number per release, each able to disagree with the others.
A derived version cannot disagree with git.
uvomits the version from the lock's own package block once it is dynamic, so thatcopy is gone too. bump-my-version resolves the current version from the newest reachable
tag instead of a file, which is the same answer hatch-vcs gives — it still proposes the
right next number, it just no longer writes it anywhere.
The pytest-rhiza pin bump is a prerequisite, not a drive-by
Making the version dynamic on its own turns the required
rhiza-testjob red. Thisrepo pinned the conformance checks at
pytest-rhiza0.2.1, which predates a rhizaproject being able to derive its version; three of its assertions read the absent
[project].versionas''and fail on it rather than skipping.0.6.0is the firstrelease that accepts a VCS-derived version, is what
rhiza-task1.7.0 already resolvesby default, and is what
jquantstatspins, having made this move first. It is in thesame commit because it edits the same file.
CITATION.cff keeps its bump entries
Two bump-my-version targets went away with the written number and neither is a loss —
the
[project]rewrite has nothing left to match, anduvomits the version from adynamic project's own package block, so the lock can no longer disagree with the
manifest after a bump, which is what that entry existed to prevent.
CITATION.cffis different and keeps its entries. Its version is a statement about acitation rather than a build input, so it has to be right in the commit the tag names —
a number no tag can supply yet. Note that
check-bumpversion-configno longer validatesthose patterns, since it skips a dynamic project entirely;
bump-my-versionstill errorsat release time if one stops matching.
Verification
Run locally, not inferred:
guess-next-dev)uvx rhiza-task@1.7.0 rhiza-testuvx rhiza-task@1.7.0 testuv lock --checkuv sync --lockedand the suite passrhiza_release.ymlat v1.8.0 already handles a derived version: it skips theuv version --shortcomparison — which exits 2 on a dynamic project — and checks thebuilt distribution's filename against the tag instead. Its build job checks out with
fetch-depth: 0, which hatch-vcs needs to see the tag.Note for the reviewer
The one behaviour that genuinely changes: between tags, a local build or editable install
now reports a dev version (
X.Y.(Z+1).devN+g<sha>) rather than the last release number.That is the point — a number that looks wrong now surfaces as a release failure instead
of as a plausible constant — but it is worth knowing before the first
pip show.🤖 Generated with Claude Code