Releasing
Cnert releases are cut from a git tag. Pushing a version tag runs
release.yaml,
which builds with uv and publishes to PyPI (trusted publishing) and
GitHub Releases.
Version source
The single source of truth is [project].version in pyproject.toml
(kept in sync in uv.lock). src/cnert/__init__.py reads
__version__ from the installed package metadata, so it never needs a
manual edit.
The rule that avoids tag/version drift:
The tag must equal
pyproject.toml's version, and must point at the commit that sets it. Bump first, commit, then tag.
Changelog
Record changes in CHANGELOG.md under ## [Unreleased] as you
work, not at release time. task release stamps that section into a
dated [X.Y.Z] section inside the release commit, so the changelog is
never updated after the fact. It refuses to release when [Unreleased]
is empty.
Release in one command
task release -- patch # 0.10.1 -> 0.10.2
task release -- minor # 0.10.1 -> 0.11.0
task release -- major # 0.10.1 -> 1.0.0
task release refuses to run on a dirty tree, then: bumps
pyproject.toml + uv.lock, commits chore: bump version to X.Y.Z,
creates an annotated tag X.Y.Z, and pushes the commit and tag. The
tag push triggers the release workflow.
Manual steps (equivalent)
task bump -- patch # edits pyproject.toml + uv.lock
python scripts/release_changelog.py "$(uv version --short)"
git commit -am "chore: release $(uv version --short)"
git tag -a "$(uv version --short)" -m "$(uv version --short)"
git push --follow-tags
What CI does
- details — parse version and suffix from the tag.
- check_pypi — abort unless the version is newer than PyPI's latest.
- setup_and_build —
uv version <tag>, thenuv build. - pypi_publish — upload to PyPI via trusted publishing
(
releaseenvironment, OIDC; no API token). - github_release — create the GitHub Release with generated notes.
Pre-releases
The workflow also accepts pre-release tags: X.Y.Za[N], X.Y.Zb[N],
X.Y.ZrcN (e.g. 1.0.0rc1). Set the matching version first, e.g.
uv version 1.0.0rc1, then commit, tag, and push as above.