Add docs/release-version-sop.md: operator SOP for cutting a versioned release — master-based flow, SemVer bump decision, version-bump PR prep, required pre-release checks, scripts/release-tag policy (safe-by-default, master-only, clean-tree, tag-after-merge), who may merge/tag, self-review/ self-merge restrictions, status:in-progress handling, worktree cleanup, and an explicit do-not list. Kept distinct from docs/release-workflows.md (future release-mcp orchestrator). Link from README. Documentation only; no code behavior changed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
6.8 KiB
Release / Version Process SOP
Operator standard operating procedure for cutting a versioned release of Gitea-Tools: version bump, checks, merge, tag, and cleanup.
Scope. This is the human/operator SOP. It is deliberately distinct from
release-workflows.md, which describes the futurerelease-mcporchestrator boundary (a coordination concept), not the day-to-day tagging process. When they disagree, this document governs how a release is actually cut today.
1. Branch flow
The repo is master-based. Releases are cut from master; there is no
separate dev/release branch unless and until that is explicitly introduced
and this SOP is updated to match. All work lands on master via reviewed PRs
from short-lived, issue-linked branches (e.g. docs/issue-68-...).
2. Where "the version" lives
There is no VERSION file and no CHANGELOG file in the repo today. The
released version is expressed only as an annotated git tag of the form
vMAJOR.MINOR.PATCH (existing tags: v1.0.0, v1.0.1). Release notes are
carried as the annotated tag's message (via --notes-file), not a tracked
changelog.
Do not confuse this with
SUPPORTED_VERSIONingitea_config.py— that is the config-schema version, unrelated to the application release version.
If a VERSION/CHANGELOG file is added later, update this SOP to list it under
"files to update".
3. Deciding the version bump (SemVer)
Pick the bump against the last tag using semantic-versioning intent:
- PATCH (
v1.0.1 → v1.0.2): bug fixes, docs, tests, internal cleanups — no change to tool names, parameters, return payloads, or behavior. - MINOR (
v1.0.1 → v1.1.0): backward-compatible additions — new MCP tool, new optional parameter, new script, additive behavior. - MAJOR (
v1.1.0 → v2.0.0): backward-incompatible changes — renamed or removed tools, changed return-payload shape, changed default behavior, or a tightened safety gate that rejects previously-accepted input.
When unsure between two levels, choose the higher one.
4. Preparing a version-bump / release PR
Releases are still gated by the normal issue-first, PR-reviewed flow.
- Open (or use) a tracking issue for the release and claim it with
status:in-progress(see §9). - Create an isolated, issue-linked branch + worktree from latest
master(e.g.chore/issue-63-v1.1.0). Never commit directly tomaster. - Include in the PR:
- Any code/docs changes that belong to the release.
- The release notes for the annotated tag (draft them in the PR body or a
notes file you will pass to
scripts/release-tag --notes-file). - If a
VERSION/CHANGELOGfile exists at that time, its update.
- Open the PR targeting
master.
The tag is not created in the PR. Tagging happens only after merge (§6).
5. Required checks before release
Run all of these green before merging the release PR and before tagging:
python3 -m py_compile mcp_server.py
python3 -m py_compile manage_labels.py
bash -n scripts/clear-provenance
./venv/bin/python -m pytest tests/ -q
git diff --check
Plus a secret sweep (there is no third-party scanner wired in; do a staged-diff
sweep — see developer-testing-guidelines.md
§7):
git diff --cached | grep -nEi "authorization: (basic|bearer)|password[:=]|token=[A-Za-z0-9]" || echo "clean"
scripts/release-tag also runs the test suite itself before tagging (unless
--skip-tests is passed), so tests are enforced twice by default.
6. Running scripts/release-tag
Tag only after the release PR is merged to master. scripts/release-tag
enforces the tagging policy and is safe by default (creates nothing on a
dry-run; never pushes without --push).
Before it tags, it requires all of:
- version matches
vMAJOR.MINOR.PATCH(SemVer); fetch --prunehas run;- you are on
master; - the worktree is clean (no uncommitted changes);
- local
masterequals<remote>/master; HEADis that same commit (the commit is present on remote master);- the tag does not already exist locally or on the remote;
- the test suite passes (unless
--skip-tests, which warns).
Typical sequence:
# 1. Dry-run to confirm the plan (changes nothing)
scripts/release-tag --dry-run v1.1.0
# 2. Create the annotated tag locally, with release notes
scripts/release-tag v1.1.0 --notes-file /path/to/release-notes.md
# 3. Push the tag only when ready
scripts/release-tag v1.1.0 --notes-file /path/to/release-notes.md --push
Env injection points (mainly for CI/tests):
RELEASE_TAG_REMOTE (default prgs), RELEASE_TAG_TEST_CMD
(default ./venv/bin/python -m pytest tests/ -q).
7. Who may merge / tag
- The release PR must be merged by someone other than its author — the author-cannot-merge safety gate applies to releases exactly as to any other PR.
- Merge uses the gated
gitea_merge_prworkflow; CLI/legacy merge is disabled. - Whoever tags must operate on clean master synced to the remote (enforced by
scripts/release-tag). Tagging is an operator action performed after merge.
8. Self-review / self-merge restrictions
Release PRs are not exempt from the safety model:
- No self-review — the author may not approve their own release PR.
- No self-merge — a different eligible identity merges.
- These gates are enforced by the MCP tooling and must not be bypassed.
9. Handling status:in-progress during release work
- Claim the release tracking issue with
status:in-progressbefore starting. - Keep it claimed while the release PR is open and under review.
- On merge/close, the tracker-hygiene automation releases
status:in-progressfor issues the PR closes; if it remains after the release lands, release it explicitly. Do not leave a shipped release issue marked in-progress.
10. Branch / worktree cleanup after merge
After the release PR merges and the tag is pushed:
-
Delete the remote release branch (if repo policy allows).
-
Remove the local worktree and delete the local branch:
git worktree remove branches/<release-worktree> git branch -d <release-branch> git worktree prune -
Confirm the root repo is clean and on
mastersynced to the remote.
11. What NOT to do
- No direct commits to
master. All changes land via reviewed PRs. - No force-push (to
masteror to tags). - No self-merge of a release PR.
- No tagging before merge — tag only commits already on remote
master. - No release from a dirty worktree —
scripts/release-tagrefuses, and so should you. - No
--skip-testsfor a real release unless there is an explicit, documented reason. - No re-tagging / moving an existing tag — pick the next version instead.