Releasing¶
Releases are cut from main by tagging. .github/workflows/release.yml does
the rest: it verifies the tag matches pyproject.toml, runs the full gate,
builds the wheel and sdist, attaches both to a GitHub Release, and publishes to
PyPI over OIDC.
One-time PyPI setup¶
Do this before the first tag. The project does not exist on PyPI yet, so it is registered as a pending publisher:
pypi.org/manage/account/publishing/ → "Add a new pending publisher"
| Field | Value |
|---|---|
| PyPI Project Name | ocx-indexbot |
| Owner | ocx-sh |
| Repository name | indexbot |
| Workflow name | release.yml |
| Environment name | pypi |
No API token is minted and nothing is stored in GitHub secrets. The release
job authenticates by OIDC from that exact workflow in that exact environment;
change any of the four values and publishing stops working, which is the
point. The GitHub pypi environment must exist with the matching name.
Cutting a release¶
task release:prepare # interactive: auto | patch | minor | major
task release:prepare BUMP=minor # skip the menu
task release:prepare BUMP=auto VERSION=1.4.0 # pin an exact version
It computes the next version from the conventional-commit history with
git-cliff, bumps pyproject.toml via uv version, regenerates
CHANGELOG.md, and runs task verify. Nothing is committed — review the
diff, then:
git add -A && git commit -m 'chore(release): vX.Y.Z'
git tag vX.Y.Z
git push --atomic origin main vX.Y.Z
--atomic matters: it pushes the commit and the tag as one operation, so a tag
can never arrive on a commit main does not have.
What the tag triggers¶
- Tag ↔ version gate.
vX.Y.Zmust equalpyproject.toml'sversion. A mismatch fails before anything is built. task verify. The same gate every pull request runs.uv build. Wheel and sdist.- GitHub Release. Both artifacts attached, notes generated.
- PyPI. Trusted Publishing,
pypienvironment.
workflow_dispatch runs steps 1–3 only — a dry run that builds and inspects
the artifacts without publishing anything.
CHANGELOG¶
CHANGELOG.md is generated by git-cliff from the commit history
(cliff.toml). Never edit it by hand — the next task changelog would
overwrite the edit. Fix the history instead, or fix cliff.toml.
task changelog:preview # what the next release section would say
task changelog # regenerate the whole file
Version policy¶
Pre-1.0. Breaking changes ship without migration shims: no deprecation warnings, no compatibility layers.
That freedom stops at the wire contract. The published URL shapes
(/config.json, /p/<ns>/<pkg>.json, /p/<ns>/<pkg>/o/sha256/<hex>.json,
/c/index.json) and their field semantics are backward compatible forever once
clients bake the endpoint — additive changes only, format_version gates the
rest. That contract belongs to ocx, not to
this package's version number.
Consumers should pin an exact version anyway: this bot runs in privileged CI jobs, so which version runs there belongs in a committed lockfile where a bump is a reviewed pull request.