# 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`](release-workflows.md), which describes the > **future `release-mcp` orchestrator** 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_VERSION` in `gitea_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. 1. Open (or use) a tracking issue for the release and **claim it** with `status:in-progress` (see §9). 2. Create an isolated, issue-linked branch + worktree from latest `master` (e.g. `chore/issue-63-v1.1.0`). Never commit directly to `master`. 3. 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`/`CHANGELOG` file exists at that time, its update. 4. 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: ```bash 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`](developer-testing-guidelines.md) §7): ```bash 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 --prune` has run; * you are **on `master`**; * the worktree is **clean** (no uncommitted changes); * local `master` **equals** `/master`; * `HEAD` is 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: ```bash # 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_pr` workflow; 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-progress` before starting. * Keep it claimed while the release PR is open and under review. * On merge/close, the tracker-hygiene automation releases `status:in-progress` for 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: ```bash git worktree remove branches/ git branch -d git worktree prune ``` * Confirm the root repo is clean and on `master` synced to the remote. ## 11. What NOT to do * **No direct commits to `master`.** All changes land via reviewed PRs. * **No force-push** (to `master` or 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-tag` refuses, and so should you. * **No `--skip-tests`** for a real release unless there is an explicit, documented reason. * **No re-tagging / moving an existing tag** — pick the next version instead.