From 9e2521f7cc0ee34ad0d537f21c816ce63e1ece22 Mon Sep 17 00:00:00 2001 From: Vinicius Queiroz Date: Thu, 16 Jul 2026 11:57:56 -0300 Subject: [PATCH] [no-issue] docs: RELEASE.md switches to PR-based release flow (main now protected) --- RELEASE.md | 41 ++++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/RELEASE.md b/RELEASE.md index 6839641..39b0b42 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -30,15 +30,42 @@ Optionally add a tag-only deployment branch policy and a required reviewer so an ## Publishing a version -1. Bump the version in `pyproject.toml`, `src/falsegreen/scanner.py` (`__version__`), and `src/falsegreen/__init__.py` in lockstep. -2. Move the `[Unreleased]` entries in `CHANGELOG.md` under the new version with today's date. Update the footer comparison links: +`main` is a protected branch: direct pushes are rejected, every change lands +through a pull request with the test CI (`test (3.8/3.11/3.13)`) green, and +commits on `main` must be signed. A release is prepared on a branch, merged via +PR, and only then tagged. The tag push and the GitHub release are not blocked by +branch protection (they act on `refs/tags/*`, not on `main`). + +### Prepare the release on a branch + +1. Branch off `main`: `git checkout main && git pull && git checkout -b release/X.Y.Z`. +2. Bump the version in lockstep in **all four** places (the `test_version_lockstep` + test fails on any mismatch): `pyproject.toml`, `src/falsegreen/scanner.py` + (`__version__`), `CITATION.cff` (`version:`), and `src/falsegreen/__init__.py` + (it re-exports `__version__` from `scanner`, so no separate string to edit, but + confirm the import still resolves). Set `CITATION.cff` `date-released` to today. +3. Move the `[Unreleased]` entries in `CHANGELOG.md` under the new version with + today's date. Update the footer comparison links: - `[Unreleased]` line: change `vPREV...HEAD` to `vX.Y.Z...HEAD` - Add `[X.Y.Z]: .../compare/vPREV...vX.Y.Z` -3. Update the pre-commit `rev` in `README.md` to `vX.Y.Z`. -4. Run the self-scan: `python -m falsegreen src tests`. It must report zero HIGH findings before the tag is created. -5. Commit everything: `git add -A && git commit -m "release: X.Y.Z"`. -6. Tag and push: `git tag -a vX.Y.Z -m "falsegreen vX.Y.Z" && git push origin main --tags`. -7. Create the GitHub release: `gh release create vX.Y.Z --generate-notes` (or paste the CHANGELOG section manually). Publishing the release fires `release.yml`, which builds and uploads to PyPI. +4. Update the pre-commit `rev` in `README.md` to `vX.Y.Z`. +5. Run the self-scan: `python -m falsegreen src tests`. It must report zero HIGH + findings. Run `pytest -q` and `ruff check src tests` too. +6. Commit (signed) and push the branch: `git commit -S -am "[no-issue] release: X.Y.Z"` + then `git push -u origin release/X.Y.Z`. + +### Merge and publish + +7. Open the PR: `gh pr create --base main --title "[no-issue] release: X.Y.Z"`. + Wait for the three `test (...)` checks to pass, then merge (squash): + `gh pr merge --squash --delete-branch`. The squash commit on `main` is signed + by GitHub (verified), satisfying the signed-commits rule. +8. Sync and tag the **merged** commit on `main` (not the branch commit — squash + creates a new SHA): `git checkout main && git pull && git tag -a vX.Y.Z -m + "falsegreen vX.Y.Z" && git push origin vX.Y.Z`. +9. Create the GitHub release: `gh release create vX.Y.Z --generate-notes` (or paste + the CHANGELOG section manually). Publishing the release fires `release.yml`, + which builds and uploads to PyPI via OIDC. Confirm the version is live: