Derive the version from the git tag instead of a checked-in file - #42
Merged
Conversation
`xwmb/version.py` held a literal that had to be bumped in its own commit before every release, and nothing tied that commit to the tag the release was actually cut from. The two could disagree, and when they did the symptom was a 400 from PyPI at the very end of the release. hatch-vcs derives the version from the tag at build time and writes it to a generated `xwmb/_version.py`, so tagging *is* the bump. `version.py` becomes a shim over the generated file, with a `0.0.0+unknown` fallback for a checkout that has never been built. The tag has to be visible for that to work, so the CI and publish checkouts use `fetch-depth: 0` and Read the Docs unshallows in `post_checkout`; without it the build quietly produces a `.devN` artifact. The publish workflow also asserts that the version it built matches the tag it was fired from, which is the check that would have caught the failure mode described above. 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.
Ports the scheme from hdrake/xeos#10 to
xwmb: the version is derived from the git tag byhatch-vcsinstead of being read from a literal inxwmb/version.py.Why
xwmb/version.pyheld__version__ = "0.6.0", which had to be bumped in its own commit before every release. Nothing tied that commit to the tag the release was actually cut from, so the two could disagree — and when they did, the symptom was a400 File already existsfrom PyPI at the very end of the release. That is exactly how thexeosv0.2.1 release failed: the tag was placed one commit before the bump, the workflow built the previous version, and PyPI rejected it.With the tag as the source of truth, tagging is the bump. There is no second commit to remember and no way for the tag and the artifact to disagree.
What changed
pyproject.toml—hatch-vcsadded tobuild-system.requires;[tool.hatch.version]switches frompath = "xwmb/version.py"tosource = "vcs";local_scheme = "no-local-version"so untagged builds areX.Y.Z.devNrather than PEP 440 local versions that indexes refuse; a build hook writes the resolved version toxwmb/_version.py.xwmb/version.py— now a shim that imports from the generated_version.py, with a0.0.0+unknownfallback for a checkout that has never been built or installed.xwmb.__version__is unchanged for anything installed from a release..gitignore— ignores the generatedxwmb/_version.py..github/workflows/publish-to-pypi.yml—fetch-depth: 0(a shallow clone has no tag, so the build would silently produce a.devNartifact), plus a step asserting the built version matches the release tag.checkout/setup-pythonbumped off the long-EOL v2..github/workflows/ci.yml—fetch-depth: 0on the checkout, since the job installs the package..readthedocs.yaml—post_checkoutunshallows and fetches tags.docs/source/conf.pytitles the pages with the installed version, so without this they would read.devN.README.md— aReleasingsection documenting the tag-is-the-version procedure and the two invariants it implies.Verified locally
v9.9.9producesxwmb-9.9.9.tar.gz/.whl, no suffix, with the generatedxwmb/_version.pyinside the sdist.mainis exactly atv0.6.0, so today's build there is0.6.0— identical to what the literal produced.Follow-up needed on the feedstock
conda-forge builds with
--no-build-isolation, so the backend's requirements must be present inhost. conda-forge/xwmb-feedstock's recipe currently lists onlyhatchling; I confirmed locally that building the sdist that way with nohatch-vcsinstalled fails inhatchling.builders.plugin.interface.get_build_hooks.hatch-vcsmust be added next tohatchlingin the feedstock'shostrequirements before the next release builds.Note on untagged checkouts
A checkout without tags — a shallow clone, or a fork that never fetched them — now resolves a
.devNversion rather than the release line. That is why every checkout in CI usesfetch-depth: 0, and it is worth pushing the release tags to any fork you install from withpip install git+https://….CI is red for an unrelated, pre-existing reason
The three
buildjobs fail at collection withImportError: cannot import name 'flatten_lol' from 'xbudget', raised from theinstalled PyPI
xwmt0.2.0: that release imports a helperxbudgetremoved in0.7. It is a dependency-compatibility problem in the environment, not something this
branch touches —
mainhas not run CI since February 2026 and would fail the sameway today. Nothing here changes Python outside
version.py.What this PR is responsible for did pass in those same runs:
pip install -e .resolved the version from the tag, and
conda listreportsxwmb 0.6.1.dev2(tagv0.6.0plus the PR's merge commits). Onmainitself, which sits exactly on thetag, the build is
0.6.0.🤖 Generated with Claude Code