Branching & releasing¶
Branching model — trunk-based with short-lived branches (GitHub Flow)¶
For a project this size, trunk-based development is the right call (it's also what most high-velocity teams, including FAANG, actually run under the hood):
mainis always releasable and always green (every push runs the full CI matrix: lint, types, tests, secret scan, supply-chain).- Short-lived feature branches (
feat/…,fix/…,docs/…) for anything non-trivial, merged via PR once CI is green. Branches live hours-to-days, not weeks — no long-running divergent branches to integrate later. - Conventional Commits on every commit; history reads as a changelog.
- No
develop/release-branch ceremony — that GitFlow overhead doesn't pay for itself here. (Revisit only if we ever maintain multiple released majors.)
Versioning — Semantic Versioning¶
MAJOR.MINOR.PATCH. Pre-1.0, minor bumps may carry breaking changes; once 1.0
ships, SemVer is strict. The version lives in pyproject.toml.
Cutting a release¶
- Ensure
mainis green andCHANGELOG.md's[Unreleased]section is filled. - Bump
versioninpyproject.toml. - Move
[Unreleased]→[X.Y.Z] — YYYY-MM-DDinCHANGELOG.md; add a fresh empty[Unreleased]. - Commit (
chore(release): vX.Y.Z) and tag:git tag vX.Y.Z git push origin main vX.Y.Z - The tag triggers
.github/workflows/release.yml, which builds the sdist + wheel withuv buildand publishes a GitHub Release with those artifacts.
PyPI publishing is intentionally deferred until the public API stabilizes; the release workflow already produces the distributable artifacts, so wiring a trusted-publisher PyPI step later is a one-job addition.