Skip to content

Release process

This project releases from main. Create a version bump on a feature branch, merge its PR, then annotate the reviewed merge and push that tag to create the GitHub Release. The sequence is identical for patch, minor and major bumps.

Versioning policy

We follow Semantic Versioning 2.0. Run make version to read the utility version. During 0.x, minor bumps may include breaking changes. The application version is independent of the development template version; infrastructure updates do not reset it.

Tooling

Use make changelog to generate CHANGELOG.md from Conventional Commits with Commitizen. The explicit changelog_start_rev under [tool.commitizen] begins at the standalone scaffold commit 959e9578785439ba7fbb0e25e8fa8dbbc4ec43c0. Earlier extracted history stays outside the generated changelog. Keep full Git history available and preserve this boundary: it overrides the first-Copier-answers default even after enrollment. Changing this metadata follows the approval policy.

Each make bump-{patch,minor,major} target:

  1. Requires a clean working tree on a feature branch, not main, master or a detached checkout.
  2. Calls uv version --bump <segment> to update pyproject.toml and uv.lock.
  3. Generates the changelog with Commitizen and formats CHANGELOG.md with mdformat.
  4. Updates this project's installation references in tracked README.md and Markdown files beneath docs/, matching git+<Repository>[.git]@v<version>. The full URL comes from [project.urls].Repository; other repositories, branch/SHA refs, changelog pages, symlinks and untracked files stay unchanged. Older installation pins also move to the new version; no manual per-file version update is needed.
  5. Commits the metadata, changelog and updated installation examples with the normal Git hooks enabled, so the pin changes are included in PR review.

The bump prepares the release for review without creating a tag. After the PR merges, make release-tag creates v<new-version> at the reviewed merge commit on an updated, clean main. It rejects feature branches, stale local main, non-merge commits and existing tags.

Every failed step stops the command. Inspect the reported error and working tree before retrying; formatting and hook changes are not silently discarded. Neither command pushes tags or publishes to PyPI.

For coding agents, ordinary uv-managed version changes remain allowed. The complete release operation also commits and tags, so it requires release authorization and is not an automatic dependency-management exemption.

Step-by-step flow

Start with a clean working tree and an up-to-date main.

1. Create the release branch

git switch main
git pull --ff-only
git switch -c chore/bump-v<new-version>

Choose <new-version> from the current version (make version) and intended segment. Codex uses codex/bump-v<new-version> for its feature branch.

2. Bump

make bump-patch     # or bump-minor, or bump-major

Review the resulting metadata, lockfile and changelog. The command creates the bump commit locally. If you chose the wrong segment, inspect the unpushed commit and agree on a correction before changing history.

3. Push the branch only

git push -u origin chore/bump-v<new-version>

Use the branch name you created. Finish PR review before creating the tag so the release includes corrections made after the bump commit.

4. Open and merge the PR

  • Title: chore: bump version to v<new-version>.
  • Complete the PR checklist; the pre-push hook runs make check and make docs-build, so no duplicate manual run is required.
  • Review the changelog as the exact notes that will accompany this release.
  • Merge with a regular merge commit, never squash. The finalization command requires a merge commit on main.

5. Create and push the reviewed tag

Once the PR is merged, update main and review that its latest merge is the release you intend to publish:

git switch main
git pull --ff-only
make release-tag
git push origin v<new-version>

The tag points at the merged contents, including changes made during review. Never move or overwrite an existing published tag. If a historical branch tag omits changes included when its PR merged, release validation rejects it; an old branch tag with identical merged contents remains valid.

6. Automatic GitHub Release

The tag-triggered workflow runs validation code from main with read-only permissions. It verifies that the tag is annotated, matches the tagged pyproject.toml, belongs to origin/main, and includes its reviewed merge contents. It extracts the corresponding section from the tagged CHANGELOG.md. Only the separate publication job has write permission; it receives those notes as data and creates the GitHub Release. A rerun keeps an already-created release instead of duplicating it.

Inspect the workflow result in Actions and the release notes in GitHub Releases. This automation creates the GitHub Release only; package-index publishing is not configured.

Historical tags

The migration baseline is package version 0.1.0. Its v0.1.0 tag is lightweight. An older v0.2.0 tag predates the root package metadata and is preserved as historical data. Do not delete or retarget either tag. make bump-minor from 0.1.0 therefore refuses the existing v0.2.0 before changing metadata; choose the next version deliberately. The release workflow requires annotated tags for future releases and must not be replayed to republish these historical tags.

The infrastructure migration itself does not bump the version or create a release.