When to use
Use when a piece of code is "ready to ship" and the next action is mechanical: tag, build, publish, announce. Apply this checklist in order. Skipping a step is the leading cause of "the release went out and X broke".
Run the checklist on the maintainer's machine, not on CI. Releases are a maintainer activity; CI handles the build, not the decision.
Examples
For example, releasing mylib from 1.4.2 to 1.5.0:
[✓] 1. All in-flight PRs merged or punted
[✓] 2. `git checkout main && git pull` — branch is current
[✓] 3. CI green on the latest commit (run `gh run list --branch main`)
[✓] 4. `pyproject.toml`: bump version 1.4.2 → 1.5.0
[✓] 5. `CHANGELOG.md`: move [Unreleased] entry to a dated 1.5.0 section
[✓] 6. Open the version-bump PR; wait for CI; merge
[✓] 7. Tag: `git tag -s v1.5.0 -m 'v1.5.0'` (annotated + signed)
[✓] 8. Push tag: `git push origin v1.5.0`
[✓] 9. CI release workflow builds sdist + wheel, uploads to PyPI
[✓] 10. Verify on PyPI: `pip download mylib==1.5.0 --no-deps -d /tmp/x`
[✓] 11. Smoke test: `pip install mylib==1.5.0` in a fresh venv, import it
[✓] 12. Announce: GitHub release notes, mailing list, internal Slack
The signing step (7) is the only one without a parallel in a hosted
CI. If you do not have a GPG key set up yet, use --no-sign for the
first release and add signing before the next.
A patch release (1.4.2 → 1.4.3) follows the same checklist but
with a hotfix branch instead of main:
[✓] 1. `git checkout -b release/1.4.3 main`
[✓] 2. Cherry-pick the fix commit(s)
[✓] 3. Bump version, update changelog (Hotfix section)
[✓] 4. Open PR, get review, merge
[✓] 5. Tag and push from main (after merge)
[✓] 6. CI publishes; verify; announce
Pitfalls to avoid
- Do not tag a commit that has not been merged to
main. The release artifact must be reproducible from the main branch's history, not from a topic branch that may later be force-pushed or rebased. - Do not bump the version in a commit that also changes code.
Separate the version bump so
git logshows the bump as its own step andgit diff v1.4.2..v1.5.0 -- pyproject.tomlis one line. - Do not publish without verifying the artifact. The cheapest
verification is
pip download+ a smoke import in a fresh venv; ten seconds that catch the common "wheel is broken on Linux" failures. - Do not skip the changelog. "Look at the commits" is not a
changelog. Users reading release notes need the human-readable
summary, not a
git log --onelinedump. - Do not publish to PyPI before the GitHub release exists. If the GitHub release fails after the PyPI publish, you have shipped a version that no one can find the source for.
- Do not reuse a tag. If a tag was pushed by mistake, delete it and re-tag the correct commit. The history of tags is the history of releases; rewriting it silently is worse than a brief gap.
- Do not announce a release until the smoke test passes. A release announcement that says "out now, broken" is worse than a short delay.
- Do not publish the same version twice. If you need to fix a
broken release, cut a new patch version (
1.5.1), do not re-upload1.5.0. PyPI does not let you overwrite, and users with the broken version pinned will not see the fix.