Releasing¶
A release makes new functionality available to customers as a pinned version of the code repository. Customers upgrade when they choose; see What to do when a new version is released. The release history and the upgrade guides are in Releases and upgrade guidance.
Versioning¶
Versions are calculated by GitVersion (.GitVersion.yml, workflow GitHubFlow/v1, mode ContinuousDelivery):
| Branch | Pattern | Label | Increment |
|---|---|---|---|
main |
^main$ |
alpha |
Minor |
| Release | release/v<major>.<minor> |
beta |
Patch |
| Feature | feature/<name> or features/<name> |
<name> |
Inherited |
- Releases are tagged
v<major>.<minor>.<patch>on the release branch (for examplev4.12.2onrelease/v4.12). -
Bumps come only from merge messages (
commit-message-incrementing: MergeMessageOnly):Merge message contains Bump +semver: majoror+semver: breakingmajor +semver: minoror+semver: featureminor +semver: patchor+semver: fixpatch +semver: noneor+semver: skipnone -
Customers pin a release branch (
release/v4.12), so they get patch releases on that branch automatically on their next run. They change the pin to move to a new minor or major version.
A pinned release branch moves
Because entry pipelines pin release/vX.Y and not a tag, a patch merged to that branch reaches customers on their
next container + hub run. Treat every merge to a release branch as a release to all customers on that minor
version.
Container image tags¶
Get-XebiaMcpContainerTags.ps1 tags every mcp image:
| Build of | Tags |
|---|---|
| any commit | the semantic version, and sha-<short sha> |
main |
+ alpha |
| a release branch | + beta |
| a tagged release commit | + latest |
| a feature branch | + latest-<label> |
Entry pipelines run on imageRef: mcp:latest. The image is built in each customer's own ACR by their container
pipeline, from the code version they pin.
Release steps¶
flowchart LR
m["main<br/>(features merged)"] --> rb["1. Cut release/vX.Y"]
rb --> v["2. Validate<br/>Demo + customer canary"]
v --> g["3. Upgrade guide"]
g --> t["4. Tag vX.Y.0"]
t --> s["5. Sync to remotes"]
s --> n["6. Release notes"]
n --> c["7. Communicate"]
1. Cut the release branch¶
Create release/v<major>.<minor> from main when the features for the version are merged. From now on, only fixes go
to this branch.
2. Validate¶
- Run
container, thenhubandspokein Plan mode from the release branch against the Demo canary configuration, without changing the configuration. Expect no changes for existing resources (see Backwards compatibility). - Apply on Demo canary and check the new features with their flags on.
- Plan against at least one customer canary. Note every manual step you needed: those go in the upgrade guide.
Earlier releases used release-candidate tags (rc<version>, for example rc4.8.0) for this stage. Use one if you need a
fixed reference for testing.
3. Write the upgrade guide¶
Write a version-specific upgrade guide when any customer action is needed beyond bumping the version: new required variables, files to delete or rename in the configuration repository, resources or policy assignments to remove in the Azure portal, migration flags to flip, or a required order of steps.
Use this structure:
# Version <X.Y>.x – release-specific upgrade steps
## From v<previous X.Y>.x
### <Step title>
1. What to change, with the exact file path in the configuration repository
1. What to remove or check in the Azure portal, with the scope
1. Commit and push
## Expected plan
- Layer <layer>: <what you should see>, e.g. "replace of azurerm_… is expected"
Add the guide to the platform upgrade guides, under a heading Version <X.Y>.x, and add a
row to its summary table and to the table of manual steps in the release history. Existing guides
are the best examples. For how readers use them, see What to do when a new version is released.
If no guide exists for a version, nothing special is needed.
Write the guide from what you needed during validation (step 2), not from memory: every manual step on the Demo or customer canary is a step for every customer. Keep each step concrete (file path, name of the object, scope) and say which pipeline layout it applies to if it differs.
4. Tag the release¶
Tag the release commit v<major>.<minor>.<patch> on the release branch. The tagged build gets the latest image tag.
5. Sync to customer remotes¶
.github/workflows/trigger-sync.yaml runs on pushes to main, release/** and v* tags in
xmms/xms-cloud-platform-code and calls xmms/xms-code-sync (sync-dispatcher.yaml). Check that the sync ran, and
spot-check a customer remote for the new branch or tag (history of the branch updated, tag present). How the sync works,
how targets are prepared and locked down, and what to check when it fails:
Code synchronisation.
6. Write the release notes¶
Release notes tell the people who take the release what changed for them. They are not a change log of the code. Compare the new tag with the previous one, check the actual changes and not only the commit titles, and group what you find. Use the work items linked to the commits for detail. Leave out internal-only work.
# Release notes for version <version>
## New features
## Improvements
## Bug fixes
## Breaking changes
## Known issues
Link the upgrade guide from the release notes when there is one. Add a short entry for the version to the release history when it is a major or minor release, or when it needed manual steps. The conventions for the text are in Release note conventions.
7. Communicate¶
Send the release notes to the people who take the release:
- Managed customers: the product owner of each customer, and the XMMS engineers who run those platforms. Until now this was done by e-mail from the product team, with the release notes attached.
- Self-managed customers: the customer's platform team, through the channel agreed at hand-over (for example the release notes in the code repository they sync, plus a notice to their contact).
Release note conventions¶
The spoke canvas, when it was a separate product, kept a short and strict format for its release notes. The same conventions work for the platform notes, and they are a good base for a self-managed platform team that writes its own notes.
| Rule | Why |
|---|---|
| Write for the person who takes the release, in plain language | The readers are product owners and engineers of the platform team, not the authors of the code |
| Say what changed for the user, not how it was built. No code references, no internal names | Implementation details do not help the reader decide or act |
| Group related changes into one line | Ten commits for one feature are one new feature |
| Keep every item short: one line, one change | The notes must be scannable |
| Sort under New features, Improvements and Bug fixes | Readers look first for what is new, then what is fixed |
Name the version and the bump in the title, for example Spoke Canvas version 2.6.0 - Minor Release |
The reader sees at once how much to expect |
| Leave out a heading that has no items | A patch release normally has only bug fixes |
| If you are unsure what a change does for the user, ask the author | A wrong note is worse than a late one |
| Do not change files of the repository while you draft them | Notes are output, not code. Review the draft and then publish it where your team keeps the notes |
How to prepare the notes:
- Decide the version: take the number from the release plan, or derive it from the latest tag and the type of release (major, minor or patch).
- Make sure you are on the release branch and have fetched the latest tags.
- Find the latest release tag (
v*) and compare it with the current head. - Read the changes themselves. Titles and messages are a start, not the source.
- Draft the notes in the format below, in the voice of the examples.
A draft for a minor release of the spoke canvas looked like this. The items are short and describe a result:
# Spoke Canvas version 2.6.0 - Minor Release
## New features
- Support federated credentials using Workload Identity Federation
- Support checking out a different repository for spoke configuration
## Improvements
- Use server pool for approval job
- Create the Azure storage container for the Terraform state files if it does not exist
A patch release has only the sections that apply:
# Spoke Canvas version 2.8.2 - Patch Release
## Bug fixes
- Changes to `required_resource_access` for the spoke service principal are now ignored to prevent unnecessary updates
Let an assistant draft, and a person decide
The spoke canvas repository used a prompt for an AI assistant that produced the draft as a snippet in chat from the
difference between the latest tag and main. This is a useful time saver, but the author still checks every item
against the real change, and decides what is a breaking change or a known issue. Those two sections of the platform
format need a human judgement.
Patch releases¶
- Fix on a branch from
main, merge with+semver: patch. - Cherry-pick the fix to each supported
release/vX.Ybranch (via a pull request to that branch). - Validate on Demo canary from the release branch, and tag
vX.Y.<patch+1>. - Add a line to the release notes of that version, under Bug fixes, in the style of the release note conventions.
Release state today¶
Observed in the repositories on 15 October 2026:
| Item | State |
|---|---|
| Latest tags | v4.12.2, v4.12.1, v4.12.0, v4.11.4 |
| Release branches | release/v4.9, release/v4.10, release/v4.11, release/v4.12 |
| Reference configuration repository (XDEV) | pins release/v4.11 |
| Release notes | Kept as notes up to v4.6.1; the overview page of those notes says "current stable v4.4.0" (out of date). Condensed in the release history. No notes exist for v4.7 and later |
| Upgrade guides | Up to v4.12.x, in Platform upgrade guides |