Release Workflow (Maintainers Only)

The release workflow includes two tools to help automate the process. Towncrier aggregates all files in newsfragments/ into CHANGELOG.md, then deletes the used fragment files. Bump My Version updates the version in the appropriate files.

This workflow produces:

  • one release commit (version bumps + changelog),

  • one git tag (vX.Y.Z).

Note:

  • Do not run this on forks of the project

  • This requires tag permissions

Pre-Release Actions

When it is determined that a new release should be made, these steps should be taken.

  • Ensure all tests are passing

  • Ensure all documentation is generated

  • Ensure all examples run

Prerequisites

Make sure the working tree is ready for a release

  • Up to date with upstream main

  • Verify tests and basic install/use

  • Release environment available (uv, towncrier, bump-my-version)

  • Decide on the type of release (patch, minor, or major)

  • Review/edit fragment wording for clarity

  • Call out breaking changes explicitly (if any)

  • Ensure release notes are understandable to users

Step-By-Step Release

At release time, the following steps need to be completed.

1) Sync and create a release branch

git checkout main
git pull --ff-only
git checkout -b release/next

2) Verify news fragments exist

uv run towncrier check

This should report no problems. If this reports any problems, add/fix fragments in newsfragments/ before continuing.

3) Check clean git tree and bump the version (updates files, no commit/tag yet)

Check to make sure the git tree is ready for release. If the following command prints anything, fix it before continuing:

git status --porcelain

Choose one based on the type of release:

  • major release: uv run bump-my-version bump major --no-commit --no-tag

  • minor release: uv run bump-my-version bump minor --no-commit --no-tag

  • patch release: uv run bump-my-version bump patch --no-commit --no-tag

This updates the version in all configured files (e.g. pyproject.toml, sphinx/source/conf.py).

4) Read the new version from pyproject.toml

NEW_VERSION="$(python - <<'PY'
import tomllib
with open("pyproject.toml","rb") as f:
    print(tomllib.load(f)["project"]["version"])
PY
)"
echo "Releasing $NEW_VERSION"

5) Preview the changelog (no files modified)

uv run towncrier build --draft --version "$NEW_VERSION"

Review the output for wording and grouping. If needed, edit fragments (or add a missing fragment).

6) Build the changelog (writes project CHANGELOG and removes fragments)

uv run towncrier build --version "$NEW_VERSION" --yes

Optional: specify a date explicitly:

uv run towncrier build --version "$NEW_VERSION" --date 2026-02-13 --yes

NOTE: Towncrier must write to the canonical root ./CHANGELOG.md.

7) Run the Full Check Suite

The following script runs type-checking, linting, and the test suite. Make sure there are no errors or warnings.

./scripts/run_checks.sh

8) Commit the release changes

git add .copier-answers.yml CITATION.cff src/buffalo_example/__init__.py pyproject.toml CHANGELOG.md newsfragments/ sphinx/source/conf.py uv.lock
git commit -m "Bump to new version -> v$NEW_VERSION"

9) Push branch

git push -u origin HEAD

10) Create the Codeberg release PR

On Codeberg, create a release PR for tag v$NEW_VERSION from the release branch into main, merge it once CI is green. Use the CHANGELOG.md section for $NEW_VERSION as the release notes.

11) Tag the release

After PR merged then can tag it.

git checkout main
git pull --ff-only
git tag -a "v$NEW_VERSION" -m "Release v$NEW_VERSION"
git push origin "v$NEW_VERSION"

12) Release to PyPI

If Codeberg/forgejo workflow queues are not clearning quickly then see PyPI Release Workflow for steps to take to manually publish the packages.

13) Publish Documentation

If Codeberg/forgejo workflow queues are not clearing quickly then see Docs Release Workflow for steps to take to manually publish the documentation.