From e4875703456a0419a0dc0ca10c5d7c59ad0f56a3 Mon Sep 17 00:00:00 2001 From: TomikeDS <203535757+TomikeDS@users.noreply.github.com> Date: Thu, 15 Oct 2026 21:50:33 +0100 Subject: [PATCH] docs(governance): add Git tagging and release branching policies --- Governance/policies/RELEASE_BRANCHING.md | 156 +++++++++++++++++++++++ Governance/policies/TAGGING.md | 134 +++++++++++++++++++ 2 files changed, 290 insertions(+) create mode 100644 Governance/policies/RELEASE_BRANCHING.md create mode 100644 Governance/policies/TAGGING.md diff --git a/Governance/policies/RELEASE_BRANCHING.md b/Governance/policies/RELEASE_BRANCHING.md new file mode 100644 index 0000000..f053bdd --- /dev/null +++ b/Governance/policies/RELEASE_BRANCHING.md @@ -0,0 +1,156 @@ +# Release Branching Policy + +## Purpose + +This policy defines the branching model for releases: when a release branch is cut, how +a release candidate is stabilised, and how a fix reaches an older supported line. It +keeps `main` open for ongoing work while a candidate is being hardened, and gives +security fixes a reliable path to the lines users are still running. + +## Scope + +This policy covers `main` and the `release/*` branches, and the flow of changes between +them. It works with the [Release Cadence](../processes/RELEASE_CADENCE.md) (the +two-week train and the 48-hour freeze), the [Versioning Policy](VERSIONING.md), the +[Tagging Policy](TAGGING.md), and the supported-lines rule in the +[Security Policy](../SECURITY_POLICY.md). It does not cover how contributors organise +their own feature branches. + +## Branching Model + +The project is **trunk-based**. `main` is the integration branch and the source of every +release. Release branches are short-lived and exist only to stabilise a candidate or to +maintain a supported older line. + +| Branch | Purpose | Lifetime | +| --- | --- | --- | +| `main` | All development. Releases are cut from it | Permanent | +| `release/MAJOR.MINOR` | Stabilisation of a candidate, then maintenance of that line while it is supported. Example: `release/1.17` | Until the line is no longer supported | +| Feature and fix branches | One change, opened as a pull request to `main` | Until merged | + +There are no long-lived `develop` or per-feature release branches. A release branch is +named after the minor line (`release/x.y`), as the +[Release Manager role](../roles/RELEASE_MANAGER.md) already describes. + +All branches that receive merges follow the same rules as `main`: changes arrive through +a pull request with the required checks and approvals, and nobody pushes or force-pushes +directly. Releases are merged by squash or rebase as set out in the +[Merge Strategy Policy](MERGE_STRATEGY.md), which keeps history linear. + +## When a Release Branch Is Cut + +A release branch is cut when a candidate is frozen for stabilisation, which is the start +of the final 48 hours before promotion described in the Release Cadence. The +[Release Manager](../roles/RELEASE_MANAGER.md) cuts it, with the Maintainers notified. + +### Cut steps + +1. Confirm that the release issue lists the candidate scope and the commit on `main` to + cut from, and that required checks are green on that commit. +2. Create the branch from that exact commit: + + ```bash + git switch main && git pull --ff-only + git switch -c release/1.17 + git push origin release/1.17 + ``` + +3. Record the branch and commit in the release issue. +4. Announce the freeze. From now on `main` accepts the next train's work, and the + candidate changes only on the release branch. + +A patch release does not need a new branch. It is made on the existing `release/x.y` +branch for that line. + +## Stabilisation + +While the candidate is on the branch: + +- Only release-blocking fixes are accepted: security, privacy, build, and data-integrity + problems, in line with the freeze rules in the Release Cadence. Features and + refactors wait for the next train. +- Each fix **lands on `main` first**, and is then brought onto the release branch (see + [Backport Flow](#backport-flow)). A fix that exists only on a release branch is a + defect, because the next release would lose it. +- Release-candidate builds are tagged `vX.Y.Z-rc.N` from the branch tip, as set out in + the [Tagging Policy](TAGGING.md), and validated through the required internal, beta, + and store channels. +- Every change on the branch needs the release owner's approval and a fresh validation + pass, as the freeze rules require. + +### Promotion + +When the Release Manager and the Maintainers approve the candidate, the Release Manager +tags the branch tip `vX.Y.Z` and publishes the release. The `package.json` version on the +branch at that commit matches the tag. The branch then stays as the maintenance line for +`X.Y`. + +## Backport Flow + +A fix reaches a supported older line by being **cherry-picked from `main`**, never by +merging the release branch into `main`. + +1. **Fix on `main` first.** The fix merges to `main` through the normal process. +2. **Decide to backport.** The fix is backported only if it is a security fix, a + data-loss or privacy fix, or a build-breaking fix for a supported line. The line must + be supported: the current line, and the previous line only while the current line is + less than two weeks old, as the Security Policy states. Features are never backported. +3. **Label.** Add `backport/X.Y` to the merged pull request. +4. **Cherry-pick** onto a branch from `release/X.Y`, keeping a reference to the original: + + ```bash + git switch -c backport/1.16-fix-token-refresh release/1.16 + git cherry-pick -x + ``` + + The `-x` flag records the source commit in the message. Keep any `Signed-off-by` + lines; add `-s` if cherry-picking would otherwise drop them. +5. **Pull request** to `release/X.Y`, with the same checks and approvals as for `main`, + linking the original pull request. +6. **Patch release.** The Release Manager tags the next patch version on the branch + (for example `v1.16.5`) after the Security Officer signs off a security fix. + +A backport that does not apply cleanly is reworked on the release branch and the +difference from the original is explained in the pull request. + +## Ending a Line + +When a line stops being supported under the Security Policy, the Release Manager records +that in the release issue and removes the `release/X.Y` branch protection and branch. +Its tags and releases are kept permanently. + +## Exceptions + +An emergency fix follows the [emergency override](APPROVAL_REQUIREMENTS.md#emergency-overrides) +in the approval requirements. It may skip nothing about branch flow: it still lands on +`main` first and is then backported, with the review window shortened rather than the +order changed. + +## Current state + +This section records what is true today so the policy is not read as describing +machinery that exists. + +- The repository has no `release/*` branches. All releases so far have been made from + `main`. +- `.github/workflows/release.yml` runs on pushes to `main` and bumps the version, creates + the release, and builds from there. It does not run for `release/*` branches. +- Branch protection for `release/*` is not configured by anything in the repository. + +Adopting this policy therefore means the Release Manager cuts and protects release +branches by hand when the first candidate needs one, and the release workflow is +extended to handle `release/*` as a follow-up owned by the Release Manager. Until then, +a release that does not need stabilisation may continue to be cut from `main` directly. + +## Ownership + +The release-management team owns this policy. Changes are proposed in a pull request +that touches only the `Governance/` folder. + +## Related Documentation + +- [Release Cadence](../processes/RELEASE_CADENCE.md) +- [Versioning Policy](VERSIONING.md) and [Tagging Policy](TAGGING.md) +- [Merge Strategy Policy](MERGE_STRATEGY.md) +- [Security Policy](../SECURITY_POLICY.md) +- [Release Manager role](../roles/RELEASE_MANAGER.md) diff --git a/Governance/policies/TAGGING.md b/Governance/policies/TAGGING.md new file mode 100644 index 0000000..e3688e9 --- /dev/null +++ b/Governance/policies/TAGGING.md @@ -0,0 +1,134 @@ +# Git Tagging Policy + +## Purpose + +This policy defines how releases are marked in Git: how tags are named, who may create +them, and the signing requirement. A tag is the permanent pointer from a published +version to the exact commit it was built from. Consistent, protected tags let anyone +reproduce a release, trace a store build back to source, and trust that a version +number never quietly changes meaning. + +## Scope + +This policy covers Git tags in this repository, and the GitHub releases attached to +them. It applies to release tags and pre-release tags. Version numbering rules +themselves belong to the [Versioning Policy](VERSIONING.md), and the release schedule to +the [Release Cadence](../processes/RELEASE_CADENCE.md); this policy only governs the +tag. + +## Naming Convention + +A tag name is the version name with a leading `v`. + +| Kind | Format | Example | Defined by | +| --- | --- | --- | --- | +| Stable release | `vMAJOR.MINOR.PATCH` | `v1.16.4` | [Versioning Policy](VERSIONING.md) | +| Alpha pre-release | `vMAJOR.MINOR.PATCH-alpha.N` | `v1.17.0-alpha.1` | Versioning Policy, pre-release conventions | +| Beta pre-release | `vMAJOR.MINOR.PATCH-beta.N` | `v1.17.0-beta.2` | same | +| Release candidate | `vMAJOR.MINOR.PATCH-rc.N` | `v1.17.0-rc.1` | same | + +Rules: + +- The existing tags (`v1.6.0` through `v1.16.4`) follow the stable format, and new tags + continue it. +- The version in the tag equals the `version` in `package.json` at the tagged commit. A + tag that disagrees with `package.json` is a defect. +- `N` starts at 1 and increases within a stage. A tag name is never reused. +- No other tag names are created for releases. Tags that mark something other than a + version, such as a build or a store submission, are not made; that information + belongs in the release issue. + +## Who May Tag + +| Tag | Who may create it | +| --- | --- | +| Stable and release-candidate tags | The [Release Manager](../roles/RELEASE_MANAGER.md), or the release automation acting on the Release Manager's behalf | +| Alpha and beta tags | The Release Manager, or a [Maintainer](../roles/MAINTAINER.md) | +| A tag on a security or hotfix release | The Release Manager with the [Security Officer](../roles/SECURITY_OFFICER.md) for security fixes, or a Maintainer for critical bugs, as set out in the Release Manager's sign-off authority | + +Nobody else tags. A contributor who needs a release marked asks in the release issue. +Tags are created only on a commit that is already on `main` or on a supported release +branch (see [Release Branching](RELEASE_BRANCHING.md)), after the required checks and +approvals for that commit have passed. + +## Tag Contents + +A release tag is an **annotated** tag. Its message states: + +- the version and the date; +- the release issue it belongs to; and +- the commit it was built from, when the tag is created from a release branch. + +Annotated tags carry an author, a date, and a message, and can be signed. Lightweight +tags carry none of these and are not used for releases. + +## Signing Requirement + +Release tags are **signed** by the person who creates them, with a key registered to +their GitHub account, so that anyone can verify who published a version: + +```bash +git tag -s v1.17.0 -m "Release v1.17.0 (release issue #NNNN)" +git tag -v v1.17.0 +git push origin v1.17.0 +``` + +This is the release-tag requirement of the [Commit Signing Policy](COMMIT_SIGNING.md), and +the key rules there apply. A signed tag does not replace the commit-level checks already +required for merging, and it is different from the signing keys used for app builds, which are governed by +[App Signing Keys](../domains/APP_SIGNING_KEYS.md). + +### Enforcement status + +This section states what is and is not true today, so the policy is not mistaken for a +description of a control that exists. + +- The existing tags are **lightweight**, not annotated: for example `v1.6.0` through + `v1.9.0` point directly at a commit with no tag object. They carry no message and no + signature. +- `.github/workflows/release.yml` creates the GitHub release and its tag with + `softprops/action-gh-release`, using `tag_name: v`. That step does not + create a signed, annotated tag. + +So the signing requirement applies from the date this policy is adopted and is **not yet +met by the release automation**. Until the workflow is changed, the Release Manager +creates the signed annotated tag by hand before publishing the release, and records that +in the release issue. Changing the automation is a follow-up owned by the Release +Manager. + +## Immutability + +A pushed tag is permanent. + +- A tag is never moved, re-pointed, force-pushed, or deleted to "fix" a release. +- If a release is wrong, a new patch version is released with a new tag. The wrong + release is marked as superseded in its release notes. +- A tag may be deleted only if it exposes something that must not be public, such as a + secret committed by mistake, or is a legal requirement. That needs two Maintainers and + the Security Officer, and the reason and the actions taken are recorded in a restricted + issue. The version number is not reused. + +Maintainers should enable GitHub tag protection for the pattern `v*` so that creating, +updating, and deleting these tags is restricted to the roles above. As with other +repository settings, this is confirmed at each release. + +## Releases and Tags Together + +Every published GitHub release has exactly one tag, and every stable tag has a GitHub +release. The release notes follow the [Changelog Policy](CHANGELOG_POLICY.md). A +pre-release tag is marked as a pre-release on GitHub and is never advertised as a stable +version. + +## Ownership + +The release-management team owns this policy. Changes are proposed in a pull request +that touches only the `Governance/` folder. + +## Related Documentation + +- [Versioning Policy](VERSIONING.md) and [Changelog Policy](CHANGELOG_POLICY.md) +- [Release Branching Policy](RELEASE_BRANCHING.md) +- [Commit Signing Policy](COMMIT_SIGNING.md) +- [Release Cadence](../processes/RELEASE_CADENCE.md) +- [Release Manager role](../roles/RELEASE_MANAGER.md) +- [App Signing Keys](../domains/APP_SIGNING_KEYS.md)