From de7be0d07a127f93ac631aff2451349ba544ad06 Mon Sep 17 00:00:00 2001 From: Mauro Servienti Date: Sun, 1 Nov 2026 17:31:08 +0100 Subject: [PATCH 1/3] Document the release process Describe the branching model, where changes go and how they are backported, how release branches and new majors start, how releases are tagged and published, and how the ServiceComposer repositories relate. --- RELEASING.md | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 RELEASING.md diff --git a/RELEASING.md b/RELEASING.md new file mode 100644 index 0000000..0be31c4 --- /dev/null +++ b/RELEASING.md @@ -0,0 +1,92 @@ +# Releasing + +This is the release process for `ServiceComposer.AspNetCore.Testing`. [ServiceComposer.AspNetCore](https://github.com/ServiceComposer/ServiceComposer.AspNetCore/blob/master/RELEASING.md) uses the same tagging and publishing steps, and also maintains previous majors on release branches. + +## Branches and versions + +Versions are calculated by [MinVer](https://github.com/adamralph/minver) from git tags. Nothing else sets a version number. + +- **`master`** is where development happens, and it builds the current major. Releases are tagged on `master`. `MinVerMinimumMajorMinor`, in `src/ServiceComposer.AspNetCore.Testing/ServiceComposer.AspNetCore.Testing.csproj`, is set to that major. +- **`release-X.Y`** branches are only needed to patch a version when `master` has moved on. The tags for those releases go on the release branch, never on `master`. + +The existing `release-1.3` and `release-2.0` branches predate the current CI workflow: `release-1.3` has no `ci.yml` and `release-2.0` publishes to MyGet. Bring the workflow up to date before shipping from either. + +MinVer derives untagged versions from the latest tag reachable from the commit. For example, `master` builds `4.2.1-alpha.0.` after `4.2.0`, even if the next release from it is `4.3.0`: the tag decides the version. + +To see which release branches exist, and what each branch builds: + +```shell +git branch -r --list 'origin/release-*' +dotnet build src/ServiceComposer.AspNetCore.Testing/ServiceComposer.AspNetCore.Testing.csproj -t:MinVer -getProperty:MinVerVersion +``` + +## Where changes go + +1. Every change goes to `master` first, through a pull request. +2. If a change also has to ship from a release branch, cherry-pick it onto a branch created from `release-X.Y` and open a pull request against `release-X.Y`: + + ```shell + git switch -c backport--to-X.Y origin/release-X.Y + git cherry-pick -x ... + ``` + + `-x` records the original commit in the message. Include any earlier commits the change depends on. +3. Don't backport: + - Dependabot dependency updates, unless the release needs them. + - Workflows that only run from the default branch, such as the project board one. + +Anything merged to `master` after a release branch is created is **not** in that release unless it's backported. + +Dependabot only updates `master`, so release branches keep their own dependency set. + +## Creating a release branch + +To patch `X.Y` after `master` has moved on, create `release-X.Y` from the `X.Y.0` tag, if it doesn't exist yet: + +```shell +git switch -c release-X.Y X.Y.0 +git push -u origin release-X.Y +``` + +## Starting a new major + +To start major `X` on `master`: + +1. If the previous major can still need patches from its latest commit on `master`, create its release branch first. +2. Open a pull request on `master` that sets `MinVerMinimumMajorMinor` to `X.0`. +3. Tag pre-releases, if any, then `X.0.0`, on `master`, as described below. + +## Tagging a release + +1. Pick the branch: `master`, unless patching a version `master` has moved past. +2. Make sure everything that should ship is merged into it, including backports, and that its CI build is green. A new vulnerability advisory or SDK update can break a build that used to pass, so if the branch has been quiet, run CI again first: + + ```shell + gh workflow run ci.yml --ref master + ``` + +3. A stable release must not depend on a pre-release. NuGet warns about it when packing (NU5104). +4. Tag the branch and push the tag: + + ```shell + git switch master # or release-X.Y + git pull + git tag X.Y.Z # or X.Y.Z-beta.N, X.Y.Z-rc.N + git push origin X.Y.Z + ``` + + Tags have no `v` prefix. The CI workflow only runs on tags matching `[0-9].[0-9]+.[0-9]` and `[0-9].[0-9]+.[0-9]+-*`, so a stable tag with a two-digit major or patch, such as `4.2.10`, doesn't run it. Update the filters in `.github/workflows/ci.yml` first, on the branch being tagged. +5. The tag build runs the tests and publishes the package: + - pre-release tags (`X.Y.Z-*`) go to the [Feedz.io pre-releases feed](https://f.feedz.io/servicecomposer/pre-releases/nuget/index.json) + - stable tags (`X.Y.Z`) go to [NuGet.org](https://www.nuget.org/profiles/ServiceComposer), through [trusted publishing](https://learn.microsoft.com/en-us/nuget/nuget-org/trusted-publishing): the workflow file name, `ci.yml`, is part of the nuget.org policy, so renaming the file requires updating the policy. +6. Create the GitHub release from the tag, with notes generated since the previous release: + + ```shell + gh release create X.Y.Z --verify-tag --generate-notes --notes-start-tag + ``` + + Add `--prerelease` for pre-release tags, and `--latest=false` when releasing an older line. Call out breaking changes, such as a new minimum .NET version, at the top of the notes. + +## Releasing across repositories + +`ServiceComposer.AspNetCore` and `ServiceComposer.AspNetCore.Testing` are released independently, and their version numbers aren't aligned. Neither package depends on the other: the tests in `ServiceComposer.AspNetCore` use this package from NuGet.org, and Renovate only proposes patch updates for it there, so that reference has to be updated manually to pick up a new minor or major. From 148f3ea8917f90a5ab444eb384cbe8e536ab5321 Mon Sep 17 00:00:00 2001 From: Mauro Servienti Date: Sun, 1 Nov 2026 17:32:33 +0100 Subject: [PATCH 2/3] Link RELEASING.md from the README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 72ef0b4..fc7a9d4 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,8 @@ ServiceComposer, a ViewModel Composition API Gateway Documentation is available @ [https://servicecomposer.github.io/ServiceComposer.AspNetCore.Testing/](https://servicecomposer.github.io/ServiceComposer.AspNetCore.Testing/). +For how releases and backports are made, see [RELEASING.md](RELEASING.md). + ### Icon [API](‪https://thenounproject.com/term/api/883169‬) by [Guilherme Simoes](https://thenounproject.com/uberux/) from [the Noun Project](https://thenounproject.com/). From 34f4350db84be366ae7788602ed1606130761bf7 Mon Sep 17 00:00:00 2001 From: Mauro Servienti Date: Sun, 1 Nov 2026 17:36:54 +0100 Subject: [PATCH 3/3] Align the release process with Radical --- RELEASING.md | 61 +++++++++++++++++++++++----------------------------- 1 file changed, 27 insertions(+), 34 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index 0be31c4..7ffe410 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -1,17 +1,13 @@ # Releasing -This is the release process for `ServiceComposer.AspNetCore.Testing`. [ServiceComposer.AspNetCore](https://github.com/ServiceComposer/ServiceComposer.AspNetCore/blob/master/RELEASING.md) uses the same tagging and publishing steps, and also maintains previous majors on release branches. +This repository follows the same release process as the other ServiceComposer repository: [ServiceComposer.AspNetCore](https://github.com/ServiceComposer/ServiceComposer.AspNetCore/blob/master/RELEASING.md). ## Branches and versions Versions are calculated by [MinVer](https://github.com/adamralph/minver) from git tags. Nothing else sets a version number. -- **`master`** is where development happens, and it builds the current major. Releases are tagged on `master`. `MinVerMinimumMajorMinor`, in `src/ServiceComposer.AspNetCore.Testing/ServiceComposer.AspNetCore.Testing.csproj`, is set to that major. -- **`release-X.Y`** branches are only needed to patch a version when `master` has moved on. The tags for those releases go on the release branch, never on `master`. - -The existing `release-1.3` and `release-2.0` branches predate the current CI workflow: `release-1.3` has no `ci.yml` and `release-2.0` publishes to MyGet. Bring the workflow up to date before shipping from either. - -MinVer derives untagged versions from the latest tag reachable from the commit. For example, `master` builds `4.2.1-alpha.0.` after `4.2.0`, even if the next release from it is `4.3.0`: the tag decides the version. +- **`master`** is where development happens, and it always builds the **next major** that hasn't been branched yet. Its `MinVerMinimumMajorMinor`, in `src/ServiceComposer.AspNetCore.Testing/ServiceComposer.AspNetCore.Testing.csproj`, is set to that major, so untagged builds are versioned `N.0.0-alpha.0.`. +- **`release-X.Y`** branches stabilize and patch an `X.Y` release. Betas, release candidates, the final release, and patch releases are all tagged on the release branch, never on `master`. To see which release branches exist, and what each branch builds: @@ -20,10 +16,12 @@ git branch -r --list 'origin/release-*' dotnet build src/ServiceComposer.AspNetCore.Testing/ServiceComposer.AspNetCore.Testing.csproj -t:MinVer -getProperty:MinVerVersion ``` +Versions released before this process was adopted were tagged on `master` and may have no release branch. To patch one, create `release-X.Y` from its `X.Y.0` tag. + ## Where changes go 1. Every change goes to `master` first, through a pull request. -2. If a change also has to ship from a release branch, cherry-pick it onto a branch created from `release-X.Y` and open a pull request against `release-X.Y`: +2. If a change also has to ship in a release that's being prepared, cherry-pick it onto a branch created from `release-X.Y` and open a pull request against `release-X.Y`: ```shell git switch -c backport--to-X.Y origin/release-X.Y @@ -32,61 +30,56 @@ dotnet build src/ServiceComposer.AspNetCore.Testing/ServiceComposer.AspNetCore.T `-x` records the original commit in the message. Include any earlier commits the change depends on. 3. Don't backport: - - Dependabot dependency updates, unless the release needs them. + - Dependabot dependency updates, unless the release needs them: release branches keep their own dependency set. - Workflows that only run from the default branch, such as the project board one. Anything merged to `master` after a release branch is created is **not** in that release unless it's backported. -Dependabot only updates `master`, so release branches keep their own dependency set. - -## Creating a release branch +## Starting a new major -To patch `X.Y` after `master` has moved on, create `release-X.Y` from the `X.Y.0` tag, if it doesn't exist yet: +To start stabilizing major `X` (for example, the first `X.0.0-beta.1`): -```shell -git switch -c release-X.Y X.Y.0 -git push -u origin release-X.Y -``` +1. On `master`, `MinVerMinimumMajorMinor` should already be `X.0`. +2. Create `release-X.0` from `master` and push it. +3. Open a pull request on `master` that sets `MinVerMinimumMajorMinor` to `X+1`, so `master` moves on to the next major. +4. Tag the first pre-release on `release-X.0`, as described below. -## Starting a new major +Don't change `MinVerMinimumMajorMinor` on `master` to get a release out of it: releases come from the release branch. -To start major `X` on `master`: +## Starting a new minor -1. If the previous major can still need patches from its latest commit on `master`, create its release branch first. -2. Open a pull request on `master` that sets `MinVerMinimumMajorMinor` to `X.0`. -3. Tag pre-releases, if any, then `X.0.0`, on `master`, as described below. +To start a new minor of a major that's already branched, create `release-X.Y` from the previous minor's release branch and push it. Then backport what it should ship, as described above. ## Tagging a release -1. Pick the branch: `master`, unless patching a version `master` has moved past. -2. Make sure everything that should ship is merged into it, including backports, and that its CI build is green. A new vulnerability advisory or SDK update can break a build that used to pass, so if the branch has been quiet, run CI again first: +1. Make sure everything that should ship is merged into `release-X.Y`, including backports, and that its CI build is green. A new vulnerability advisory can break a build that used to pass, so if the branch has been quiet, run CI again first: ```shell - gh workflow run ci.yml --ref master + gh workflow run ci.yml --ref release-X.Y ``` -3. A stable release must not depend on a pre-release. NuGet warns about it when packing (NU5104). -4. Tag the branch and push the tag: +2. A stable release must not depend on a pre-release, and NuGet warns about it when packing (NU5104). If needed, raise the lower bound of the version range to the stable version first. +3. Tag the release branch and push the tag: ```shell - git switch master # or release-X.Y + git switch release-X.Y git pull git tag X.Y.Z # or X.Y.Z-beta.N, X.Y.Z-rc.N git push origin X.Y.Z ``` - Tags have no `v` prefix. The CI workflow only runs on tags matching `[0-9].[0-9]+.[0-9]` and `[0-9].[0-9]+.[0-9]+-*`, so a stable tag with a two-digit major or patch, such as `4.2.10`, doesn't run it. Update the filters in `.github/workflows/ci.yml` first, on the branch being tagged. -5. The tag build runs the tests and publishes the package: + Tags have no `v` prefix: the CI workflow only runs on tags matching `[0-9]+.[0-9]+.[0-9]+` and `[0-9]+.[0-9]+.[0-9]+-*`. +4. The tag build runs the tests and publishes the packages: - pre-release tags (`X.Y.Z-*`) go to the [Feedz.io pre-releases feed](https://f.feedz.io/servicecomposer/pre-releases/nuget/index.json) - - stable tags (`X.Y.Z`) go to [NuGet.org](https://www.nuget.org/profiles/ServiceComposer), through [trusted publishing](https://learn.microsoft.com/en-us/nuget/nuget-org/trusted-publishing): the workflow file name, `ci.yml`, is part of the nuget.org policy, so renaming the file requires updating the policy. -6. Create the GitHub release from the tag, with notes generated since the previous release: + - stable tags (`X.Y.Z`) go to [NuGet.org](https://www.nuget.org/profiles/ServiceComposer) +5. Create the GitHub release from the tag, with notes generated since the previous release: ```shell gh release create X.Y.Z --verify-tag --generate-notes --notes-start-tag ``` - Add `--prerelease` for pre-release tags, and `--latest=false` when releasing an older line. Call out breaking changes, such as a new minimum .NET version, at the top of the notes. + Add `--prerelease` for pre-release tags, and `--latest=false` when releasing an older major. Call out breaking changes, such as a new minimum .NET version, at the top of the notes. ## Releasing across repositories -`ServiceComposer.AspNetCore` and `ServiceComposer.AspNetCore.Testing` are released independently, and their version numbers aren't aligned. Neither package depends on the other: the tests in `ServiceComposer.AspNetCore` use this package from NuGet.org, and Renovate only proposes patch updates for it there, so that reference has to be updated manually to pick up a new minor or major. +`ServiceComposer.AspNetCore` and `ServiceComposer.AspNetCore.Testing` are released independently, in any order, and their version numbers aren't aligned. Neither package depends on the other. The tests in `ServiceComposer.AspNetCore` use this package from NuGet.org, and Renovate only proposes patch updates for it there, so that reference has to be updated manually to pick up a new minor or major.