Contributing
Branching model
This repo uses a two-branch flow:
main— public release branch. Every push tomainpublishes the package to PyPI (gated by a manual approval on thepypiGitHub Environment) and deploys the docs.dev— integration branch and the GitHub default branch. Versioning happens here: every push runspython-semantic-release, which inspects conventional-commit messages and creates avX.Y.Ztag if a release is warranted.
Workflow
Working on a feature or fix
- Branch off
dev. - Open a PR back into
dev. The PR title must be a valid conventional commit, e.g.:feat: add foo support(minor bump)fix: handle bar correctly(patch bump)feat!: rename bazor includeBREAKING CHANGE:in the body (major bump)docs:,chore:,refactor:,test:,ci:,style:,perf:,revert:(no version bump, but appears in changelog)
- CI runs the PR title lint and the test suite.
- Squash-merge the PR into
dev. The PR title becomes the squash commit message — this is what PSR will analyze. - On push to
dev,Release Dev Branchruns the test suite, then PSR creates the version commit and tag if appropriate.
Hotfixes
Hotfixes follow the same path as features: branch off dev, PR into dev, squash-merge. Use a fix: prefix for the patch bump. Do not push directly to main.
Promoting a release
- Open a PR from
devintomain. - CI runs the test suite as a final gate.
- Fast-forward merge (no merge commit, no squash). This carries over all dev commits and tags as-is.
- On push to
main,Release Main Branchruns tests, then enters thepypienvironment, which pauses for manual approval. - After approval, the package is built from the latest tag and published to PyPI; docs deploy at the same ref.
Commits and conventional format
The PR title is the source of truth — it becomes the squash commit on dev that PSR analyzes. Local commit messages on the feature branch don't need to be conventional.
Allowed types: feat, fix, docs, style, refactor, perf, test, ci, chore, revert.
Local development
See .claude/CLAUDE.md for the full command reference (uv, pytest, ruff, mypy, mkdocs).