Release process¶
A PyPI, crates.io, or GitHub Release upload happens only when a
vX.Y.Z tag is pushed. That is the entire publish gate.
1 Tag name¶
.github/workflows/release.yml listens for push.tags: 'v*'. The
publish jobs also require startsWith(github.ref, 'refs/tags/v').
Correct:
v0.9.0,v0.7.2Wrong:
0.8.0,release-0.9.0, a branch namedv0.9.0
An unprefixed tag such as 0.8.0 is a Git object only. It does not
start the workflow, so it does not upload wheels, does not publish
anneal-core, and does not open a GitHub Release.
Leave a mistaken unprefixed tag in place. Add the v tag on the same
commit. Do not move or delete the old name.
2 Version triple¶
validate-release reads three files and requires them to equal the
tag with the leading v stripped:
Cargo.toml[package].versionpyproject.toml[project].versionCITATION.cffversion:
If any one disagrees, the workflow must stop before wheels and
nothing is published. The validate step uses set -euo pipefail so a
failed test fails the job. Bump all three in the same
chore(release): commit.
3 Steps¶
Add towncrier fragments under
docs/newsfragments/for every user-visible change.Bump the version triple to the new
X.Y.Z.Run
pixi r towncrier-buildsoCHANGELOG.mdcarries that version.Commit. Push
main.Create an annotated, signed tag named exactly
vX.Y.Zon that commit (the same object the changelog describes, not a later campaign commit):
git tag -a vX.Y.Z <commit> -m "vX.Y.Z"
git push origin vX.Y.Z
Confirm the
Releaseworkflow on that tag is green and that https://pypi.org/project/anneal/X.Y.Z/ exists.
Do not use workflow_dispatch as a substitute for the tag. Dispatch
builds artifacts; it does not publish unless github.ref is a v* tag.
4 What the tag starts¶
On vX.Y.Z the workflow builds abi3 wheels, an sdist, and native
library tarballs, then publishes the Python artifacts to PyPI, publishes
anneal-core to crates.io if that version is absent, and opens the
GitHub Release.
5 Why 0.8.0 and 0.9.0 were missing from PyPI¶
PyPI and crates.io stopped at 0.7.2 (v0.7.2, 2026-08-01).
0.8.0(2026-08-08) was tagged0.8.0, notv0.8.0. The workflow never ran. That commit also leftCITATION.cffat0.7.2, so a laterv0.8.0on the same tree still failsvalidate-release.0.9.0had matching0.9.0metadata and assembled notes (df28dd2) and no tag at all.
The corrective tags are v0.8.0 on 0.8.0 and v0.9.0 on
df28dd2. The 0.8.0 tree’s workflow file does not set -e in
validate, so that historical tag can still build wheels even though
CITATION.cff is 0.7.2. New tags use the set -euo pipefail
validator.