About the Release Process¶
This page explains how the automated release pipeline works.
Overview¶
The release pipeline converts a Git tag into a published PyPI package through a series of automated steps with a human checkpoint:
graph LR
A[Push Tag<br/>v*.*.*] --> B[changelog.yml]
B --> C[Generate<br/>CHANGELOG.md]
B --> D[Build Package<br/>validation]
C --> E[Create PR]
E --> F[Review & Merge<br/>PR]
F --> G[publish-release.yml]
G --> H[Create GitHub<br/>Release]
H --> I{Manual<br/>Approval}
I -->|Approve| J[Publish to PyPI]
style I fill:#ff9,stroke:#333,stroke-width:2px
style J fill:#9f9,stroke:#333,stroke-width:2px
Two Separate Workflows¶
The release is split into two workflows:
- changelog.yml - Triggered by tag push, generates changelog, builds packages, creates a PR
- publish-release.yml - Triggered by PR merge, creates GitHub Release, publishes to PyPI
Each workflow has a single responsibility - preparation vs. publication. The PR between them creates a natural review point where maintainers can verify the changelog and build before anything is published.
Manual Approval Before PyPI¶
Publishing to PyPI is irreversible. The manual approval gate (via GitHub environment protection rules) provides human verification, an emergency brake for bad releases, and a clear audit trail.
Trusted Publishing (OIDC)¶
The template uses PyPI's Trusted Publishing:
- No stored secrets - authentication uses short-lived OIDC tokens generated by GitHub Actions
- Workflow-scoped - only
publish-release.ymlin the specific repository can publish - Environment-scoped - the OIDC token is only available after manual approval
Changelog Generation¶
The changelog is generated automatically from Conventional Commits using git-cliff - feat: commits appear under Added, fix: under Fixed, breaking changes are highlighted prominently. The template enforces conventional commits at three points, because different PRs ship different
text. GitHub's squash_merge_commit_title defaults to COMMIT_OR_PR_TITLE, so a single-commit
PR ships its commit message while a multi-commit PR ships its PR title — and git-cliff reads
whichever landed. A commit-msg hook catches bad messages as you write them, a CI job validates the
commit message on single-commit PRs, and PR title validation covers the multi-commit case.
Version Numbering¶
The template follows Semantic Versioning:
- MAJOR (1.0.0): Breaking changes (indicated by
!orBREAKING CHANGE:) - MINOR (0.1.0): New features (
feat:commits) - PATCH (0.0.1): Bug fixes (
fix:commits)
The version is determined by the Git tag - there is no version file to manually edit. The hatch-vcs build plugin reads the tag and sets the package version accordingly.
The Complete Flow in Practice¶
- Developer pushes a signed version tag:
git tag -s v0.2.0 -m "Release v0.2.0" && git push origin v0.2.0(tags are signed with gitsign keyless Sigstore signing, so the tag is verifiable with no long-lived GPG key; this complements the artifact-level PEP 740 attestations) changelog.ymlgenerates the changelog, builds the package, creates a PR- Maintainer reviews and merges the PR
publish-release.ymlcreates a GitHub Release with artifacts- Designated reviewer receives a notification and approves the PyPI deployment
- Package is published to PyPI via Trusted Publishing
Connections¶
- How to Set Up CI/CD Services - step-by-step setup instructions
- GitHub Workflows - workflow technical reference