feat/x ─┐ release-please (on main)
fix/y ─┼─PR (squash, conventional title)──▶ dev ──PR (merge commit)──▶ main ──▶ release PR
docs/z ─┘ │ merge
dev ◀── back-merge ────── tag vX.Y.Z + GitHub Release
mainonly ever holds released code; deployments build from avX.Y.Ztag, never a branch.devis the integration branch and the default branch: open feature PRs against it.- Feature branches:
feat/…,fix/…,docs/…,refactor/…,chore/…,ci/…— short-lived, one topic each.
- Into
dev: squash-merged. The PR title must be a conventional commit (checked bypr-title) — it becomes the single commit ondevand the line in the changelog:feat(reports): franking credits,fix(sync): retry on timeout,docs: …. Breaking:feat!: …plus aBREAKING CHANGE:note in the description. - Into
main: a release PR fromdev, merged with a merge commit (not squash) so each conventional commit reaches release-please. - Required checks:
test (3.12),test (3.14),lint, pluspr-titleon PRs into dev. Approvals aren't required (solo maintainer — GitHub doesn't count self-approval).
- Open a PR
dev→main("release: …"), wait for CI, merge (merge commit). - release-please opens or updates
chore(main): release X.Y.Zonmain: the version bump (src/pyfinbot/version.py) and the newCHANGELOG.mdsection, computed from the commits —feat→ minor,fix/perf/build→ patch (docs, refactor, tests, CI and chores alone never cut a release); while below 1.0 a breaking change bumps the minor. - Review the notes, merge it (it's opened by the
gth-release-botGitHub App, so CI runs on it). That tagsvX.Y.Z, publishes the GitHub Release with the same notes, and mergesmainback intodev.
Never bump versions or edit released CHANGELOG.md sections by hand, and never move a v* tag (the tag ruleset
blocks it).
Repository rulesets (source of truth: .github/rulesets/, applied with
scripts/apply-rulesets.sh): main and dev need a PR and green checks, no force-push or deletion; v* tags
can't be moved or deleted, and only release-please (the gth-release-bot app) creates them. In an emergency the admin can merge a PR past failing checks (a logged bypass) — but
nobody, admin included, can push straight to main or dev. Use the bypass for fixing a broken pipeline, not
for skipping one.