From 7da8a817014cbebacc0e6e78b7f6e059563c9b99 Mon Sep 17 00:00:00 2001 From: Jeff Handley Date: Mon, 13 Jul 2026 19:35:35 -0700 Subject: [PATCH 01/15] Refactor prepare-release skill for progressive disclosure - Move the detailed internal-branch preparation steps into references/stage-1-prepare-internal-branch.md - Slim SKILL.md to a staged overview that points to the Stage 1 reference file - Add a Stage workflow section requiring per-stage (and per-sub-stage) review, approval, and a separate commit Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/skills/prepare-release/SKILL.md | 128 ++---------------- .../stage-1-prepare-internal-branch.md | 124 +++++++++++++++++ 2 files changed, 137 insertions(+), 115 deletions(-) create mode 100644 .github/skills/prepare-release/references/stage-1-prepare-internal-branch.md diff --git a/.github/skills/prepare-release/SKILL.md b/.github/skills/prepare-release/SKILL.md index 33f30dbff05..78458bc465f 100644 --- a/.github/skills/prepare-release/SKILL.md +++ b/.github/skills/prepare-release/SKILL.md @@ -3,127 +3,25 @@ name: prepare-release description: Prepares the repository for an internal release branch. Use this when asked to "prepare for a release", "prepare internal release branch", or similar release preparation tasks. --- -# Prepare Internal Release Branch +# Prepare Release -When preparing a public branch for internal release, apply the following changes: +Prepares a `dotnet/extensions` public release branch (`release/.`) for the internal release process on the corresponding `internal/release/.` branch. -## 1. Directory.Build.props +The preparation is organized into ordered stages. Work through them in sequence, loading each stage's reference file only when you reach that stage. -Add NU1507 warning suppression after the `TestNetCoreTargetFrameworks` PropertyGroup. Internal branches don't use package source mapping due to internal feeds: +## Stage workflow -```xml - - - $(NoWarn);NU1507 - -``` +Complete stages strictly in order. Treat each stage -- and each sub-stage of a stage that has them -- as an independent, committable unit. For every stage or sub-stage: -Insert this new PropertyGroup right after the closing `` that contains `TestNetCoreTargetFrameworks`. +1. Apply the changes described in the stage's reference file. +2. Prompt the user to review the changes for that stage (summarize what changed and show the diff). +3. Wait for the user's approval before committing. +4. Create a single commit that contains only that stage's (or sub-stage's) changes. -## 2. NuGet.config +Every stage gets its own commit, and every sub-stage gets its own commit. Never combine multiple stages or sub-stages into a single commit. -Remove the entire `` section. This section looks like: +## Stage 1 - Prepare Internal Branch -```xml - - - - - - - - - - -``` +Apply the internal-release infrastructure changes to the branch: suppress `NU1507`, remove the NuGet package source mapping, switch on stable/release versioning, add private-feed credential setup to the build template, comment out integration tests, and remove the code-coverage pipeline stage. Never change version numbers here -- those flow via Dependency Flow automation. -**Important**: Do NOT add new internal feed sources to NuGet.config - those are managed by Dependency Flow automation and will be added automatically. - -## 3. eng/Versions.props - -Update these two properties (do NOT change any version numbers): - -Change `StabilizePackageVersion` from `false` to `true`: -```xml -true -``` - -Change `DotNetFinalVersionKind` from empty to `release`: -```xml -release -``` - -## 4. eng/pipelines/templates/BuildAndTest.yml - -### Add Private Feeds Credentials Setup - -After the Node.js setup task (the `NodeTool@0` task), add these two tasks to authenticate with private Azure DevOps feeds: - -```yaml - - task: PowerShell@2 - displayName: Setup Private Feeds Credentials - condition: eq(variables['Agent.OS'], 'Windows_NT') - inputs: - filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.ps1 - arguments: -ConfigFile $(Build.SourcesDirectory)/NuGet.config -Password $Env:Token - env: - Token: $(dn-bot-dnceng-artifact-feeds-rw) - - - task: Bash@3 - displayName: Setup Private Feeds Credentials - condition: ne(variables['Agent.OS'], 'Windows_NT') - inputs: - filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.sh - arguments: $(Build.SourcesDirectory)/NuGet.config $Token - env: - Token: $(dn-bot-dnceng-artifact-feeds-rw) -``` - -### Comment Out Integration Tests - -Comment out the integration tests step as they require authentication to private feeds that isn't available during internal release builds: - -```yaml - - ${{ if ne(parameters.skipTests, 'true') }}: - # Skipping integration tests for now as they require authentication to the private feeds - # - script: ${{ parameters.buildScript }} - # -integrationTest - # -configuration ${{ parameters.buildConfig }} - # -warnAsError 1 - # /bl:${{ parameters.repoLogPath }}/integration_tests.binlog - # $(_OfficialBuildIdArgs) - # displayName: Run integration tests -``` - -## 5. azure-pipelines.yml - -Remove the `codecoverage` stage entirely. This is the stage that: -- Has `displayName: CodeCoverage` -- Downloads code coverage reports from build jobs -- Merges and validates combined test coverage -- Contains a `CodeCoverageReport` job - -Also remove the `codecoverage` dependency from the post-build validation's `validateDependsOn` list: - -```yaml -# Remove this conditional dependency block: -- ${{ if eq(parameters.runTests, true) }}: - - codecoverage -``` - -## Files NOT to modify - -- **eng/Version.Details.xml**: Version updates are managed by Dependency Flow automation -- **eng/Versions.props version numbers**: Package versions are managed by Dependency Flow automation -- **NuGet.config feed sources**: Internal darc feeds are added automatically by Dependency Flow - -## Summary - -| File | Action | -|------|--------| -| Directory.Build.props | Add `NU1507` to `NoWarn` in new PropertyGroup | -| NuGet.config | Remove entire `` section | -| eng/Versions.props | Set `StabilizePackageVersion=true`, `DotNetFinalVersionKind=release` | -| eng/pipelines/templates/BuildAndTest.yml | Add private feeds credentials setup tasks, comment out integration tests | -| azure-pipelines.yml | Remove `codecoverage` stage and its post-build dependency | +Read and follow [references/stage-1-prepare-internal-branch.md](references/stage-1-prepare-internal-branch.md). diff --git a/.github/skills/prepare-release/references/stage-1-prepare-internal-branch.md b/.github/skills/prepare-release/references/stage-1-prepare-internal-branch.md new file mode 100644 index 00000000000..7587cf591ab --- /dev/null +++ b/.github/skills/prepare-release/references/stage-1-prepare-internal-branch.md @@ -0,0 +1,124 @@ +# Stage 1 - Prepare Internal Branch + +When preparing a public branch for internal release, apply the following changes: + +## 1. Directory.Build.props + +Add NU1507 warning suppression after the `TestNetCoreTargetFrameworks` PropertyGroup. Internal branches don't use package source mapping due to internal feeds: + +```xml + + + $(NoWarn);NU1507 + +``` + +Insert this new PropertyGroup right after the closing `` that contains `TestNetCoreTargetFrameworks`. + +## 2. NuGet.config + +Remove the entire `` section. This section looks like: + +```xml + + + + + + + + + + +``` + +**Important**: Do NOT add new internal feed sources to NuGet.config - those are managed by Dependency Flow automation and will be added automatically. + +## 3. eng/Versions.props + +Update these two properties (do NOT change any version numbers): + +Change `StabilizePackageVersion` from `false` to `true`: +```xml +true +``` + +Change `DotNetFinalVersionKind` from empty to `release`: +```xml +release +``` + +## 4. eng/pipelines/templates/BuildAndTest.yml + +### Add Private Feeds Credentials Setup + +After the Node.js setup task (the `NodeTool@0` task), add these two tasks to authenticate with the private package feeds: + +```yaml + - task: PowerShell@2 + displayName: Setup Private Feeds Credentials + condition: eq(variables['Agent.OS'], 'Windows_NT') + inputs: + filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.ps1 + arguments: -ConfigFile $(Build.SourcesDirectory)/NuGet.config -Password $Env:Token + env: + Token: $() + + - task: Bash@3 + displayName: Setup Private Feeds Credentials + condition: ne(variables['Agent.OS'], 'Windows_NT') + inputs: + filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.sh + arguments: $(Build.SourcesDirectory)/NuGet.config $Token + env: + Token: $() +``` + +### Comment Out Integration Tests + +Comment out the integration tests step as they require authentication to private feeds that isn't available during internal release builds: + +```yaml + - ${{ if ne(parameters.skipTests, 'true') }}: + # Skipping integration tests for now as they require authentication to the private feeds + # - script: ${{ parameters.buildScript }} + # -integrationTest + # -configuration ${{ parameters.buildConfig }} + # -warnAsError 1 + # /bl:${{ parameters.repoLogPath }}/integration_tests.binlog + # $(_OfficialBuildIdArgs) + # displayName: Run integration tests +``` + +## 5. azure-pipelines.yml + +Remove the `codecoverage` stage entirely. This is the stage that: +- Has `displayName: CodeCoverage` +- Downloads code coverage reports from build jobs +- Merges and validates combined test coverage +- Contains a `CodeCoverageReport` job + +Also remove the `codecoverage` dependency from the post-build validation's `validateDependsOn` list: + +```yaml +# Remove this conditional dependency block: +- ${{ if eq(parameters.runTests, true) }}: + - codecoverage +``` + +## Files NOT to modify + +- **eng/Version.Details.xml**: Version updates are managed by Dependency Flow automation +- **eng/Versions.props version numbers**: Package versions are managed by Dependency Flow automation +- **NuGet.config feed sources**: Internal darc feeds are added automatically by Dependency Flow + +## Summary + +| File | Action | +|------|--------| +| Directory.Build.props | Add `NU1507` to `NoWarn` in new PropertyGroup | +| NuGet.config | Remove entire `` section | +| eng/Versions.props | Set `StabilizePackageVersion=true`, `DotNetFinalVersionKind=release` | +| eng/pipelines/templates/BuildAndTest.yml | Add private feeds credentials setup tasks, comment out integration tests | +| azure-pipelines.yml | Remove `codecoverage` stage and its post-build dependency | From bc82c1248db2f5008a9f6e5c506fc0ac659c024c Mon Sep 17 00:00:00 2001 From: Jeff Handley Date: Mon, 13 Jul 2026 20:09:30 -0700 Subject: [PATCH 02/15] Add Stage 2 - Update Dependencies to prepare-release skill - Add references/stage-2-update-dependencies.md with .NET 9, .NET 8, and .NET 10 dependency-update sub-stages - Direct the user to release.dot.net artifacts and accept manifests/ReleaseManifest.json by paste or local path to read barBuildIds - Validate each id with darc get-build before darc update-dependencies; require darc auth prerequisite - Encode per-sub-stage commits, NuGet.config keep-both feeds, and the LTSVersion/Net10Version Versions.props edits - Revert Version.Details.xml for 8.0 and 10.0, revert eng/common for 10.0, and keep the Arcade Templating entries - Add a Stage 2 pointer section to SKILL.md Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/skills/prepare-release/SKILL.md | 12 +- .../references/stage-2-update-dependencies.md | 135 ++++++++++++++++++ 2 files changed, 144 insertions(+), 3 deletions(-) create mode 100644 .github/skills/prepare-release/references/stage-2-update-dependencies.md diff --git a/.github/skills/prepare-release/SKILL.md b/.github/skills/prepare-release/SKILL.md index 78458bc465f..df9f755c03b 100644 --- a/.github/skills/prepare-release/SKILL.md +++ b/.github/skills/prepare-release/SKILL.md @@ -14,14 +14,20 @@ The preparation is organized into ordered stages. Work through them in sequence, Complete stages strictly in order. Treat each stage -- and each sub-stage of a stage that has them -- as an independent, committable unit. For every stage or sub-stage: 1. Apply the changes described in the stage's reference file. -2. Prompt the user to review the changes for that stage (summarize what changed and show the diff). -3. Wait for the user's approval before committing. +2. Prompt the user to review the changes (summarize what changed and show the diff), unless the stage's reference file directs you to commit automatically. +3. Wait for the user's approval before committing, unless the stage's reference file directs automatic commits. 4. Create a single commit that contains only that stage's (or sub-stage's) changes. -Every stage gets its own commit, and every sub-stage gets its own commit. Never combine multiple stages or sub-stages into a single commit. +Every stage gets its own commit, and every sub-stage gets its own commit. Never combine multiple stages or sub-stages into a single commit. Never push until the user explicitly instructs it, whatever a stage's commit cadence. ## Stage 1 - Prepare Internal Branch Apply the internal-release infrastructure changes to the branch: suppress `NU1507`, remove the NuGet package source mapping, switch on stable/release versioning, add private-feed credential setup to the build template, comment out integration tests, and remove the code-coverage pipeline stage. Never change version numbers here -- those flow via Dependency Flow automation. Read and follow [references/stage-1-prepare-internal-branch.md](references/stage-1-prepare-internal-branch.md). + +## Stage 2 - Update Dependencies + +Update the branch's product dependencies to the pending .NET 9, .NET 8, and .NET 10 servicing releases using `darc update-dependencies`. The BAR build IDs come from the release.dot.net Release Tracker, which is behind Microsoft auth and unreachable by the agent, so the user supplies them (pasted `ReleaseManifest.json` or a downloaded copy). This stage has three sub-stages, each its own commit: .NET 9, then .NET 8, then .NET 10. + +Read and follow [references/stage-2-update-dependencies.md](references/stage-2-update-dependencies.md). diff --git a/.github/skills/prepare-release/references/stage-2-update-dependencies.md b/.github/skills/prepare-release/references/stage-2-update-dependencies.md new file mode 100644 index 00000000000..02a6f0dd864 --- /dev/null +++ b/.github/skills/prepare-release/references/stage-2-update-dependencies.md @@ -0,0 +1,135 @@ +# Stage 2 - Update Dependencies + +Update the internal release branch's product dependencies to the pending .NET 9, .NET 8, and .NET 10 servicing releases using `darc update-dependencies` against Build Asset Registry (BAR) build IDs. + +This stage has three sub-stages. Run them in order, and give each its own commit: + +1. Update Dependencies: .NET 9 +2. Update Dependencies: .NET 8 +3. Update Dependencies: .NET 10 + +Commit each sub-stage automatically as you complete it -- do not pause for per-sub-stage review or approval. This overrides steps 2 and 3 of the Stage workflow in `SKILL.md`. Pushing remains a separate, user-directed step: do not push until the user explicitly instructs it (see "After the stage"). + +Before committing each sub-stage, review the diff yourself and revert any incidental, non-dependency changes darc introduces (for example, darc sometimes adds a trailing newline to `.config/dotnet-tools.json`). Keep only the intended dependency edits for that sub-stage. + +## Prerequisites + +- Confirm the working tree is clean and you are on the `stage-release-.` branch. +- `darc` must be installed and authenticated against BAR: + - If `darc` is not on the PATH, run `eng/common/darc-init.ps1` (Windows) or `eng/common/darc-init.sh`. + - If darc commands fail with authentication errors, ask the user to run `darc authenticate` and supply a valid BAR token. Never store, echo, or commit tokens. + +## Gathering the BAR build IDs (release.dot.net) + +The Release Tracker at is a Blazor WebAssembly app behind Microsoft (MSAL) authentication. The agent cannot reach or render it, so the **user** provides the inputs. + +**Collect the inputs for all three sub-stages up front, in a single prompt, before running any `darc` commands.** For each pending release, the user opens it on the Release Tracker, clicks its `{}` artifacts link, and opens `manifests/ReleaseManifest.json`. Accept each one either pasted into the chat or as a local path to a downloaded copy: + +- **.NET 9** -- the pending 9.0 release's `manifests/ReleaseManifest.json`. +- **.NET 8** -- the pending 8.0 release's `manifests/ReleaseManifest.json`. +- **.NET 10** -- the pending 10.0 release's `manifests/ReleaseManifest.json`, or a pasted list of its BAR build IDs from the release page. + +Each build entry in a `ReleaseManifest.json` has a `repo` (like ``) and a `barBuildId`. Read the IDs you need: + +- **.NET 9 and .NET 8:** the `barBuildId` for `dotnet-runtime`, `dotnet-aspnetcore`, and `dotnet-efcore`. +- **.NET 10:** every `barBuildId` (or the pasted list). Order does not matter. + +Validate every ID with `darc get-build --id ` before applying it, and confirm the repository and commit look correct. If an ID does not resolve, stop and ask the user. + +## Sub-stage 1 - Update Dependencies: .NET 9 + +For each of `dotnet-runtime`, `dotnet-aspnetcore`, and `dotnet-efcore`, using that release's `barBuildId`: + +``` +darc update-dependencies --id +``` + +- `dotnet-efcore` commonly reports `warn: Found no dependencies to update` -- that is expected. +- .NET 9 is the coherent primary update: **keep everything darc changes**, including `eng/Version.Details.xml`, the non-suffixed `...Version` entries in `eng/Versions.props`, and the new `internal package sources` feeds in `NuGet.config`. + +Review the changes, then stage and commit: + +``` +git add . +git commit -m "Update 9.0 dependencies" +``` + +## Sub-stage 2 - Update Dependencies: .NET 8 + +For each of `dotnet-runtime`, `dotnet-aspnetcore`, and `dotnet-efcore`, using the 8.0 release's `barBuildId`: + +``` +darc update-dependencies --id +``` + +`dotnet-efcore` commonly reports `warn: Found no dependencies to update`. + +Once all three repos are applied, fix up the changes so that only the 8.0 (`...LTSVersion`) entries move: + +1. Revert `Version.Details.xml` (the 8.0 update must not rewrite it): + + ``` + git checkout eng/Version.Details.xml + ``` + +2. `NuGet.config` -- resolve keeping both. Keep the pre-existing `internal package sources` feeds (added by .NET 9) **and** the newly added 8.0 ones; discard the deletions and keep the additions. The result adds the new 8.0 `internal package sources` sources: three in `` and three in ``. Then: + + ``` + git add NuGet.config + ``` + +3. `eng/Versions.props` hand-edit: + - Move the 8.0 versions darc produced into the matching `...LTSVersion` entries, replacing their previous values (that is, take the updated numbers from the non-suffixed `...Version` entries and write them into the corresponding `...LTSVersion` entries -- "Version>" becomes "LTSVersion>"). + - Revert the changes to the 9.0 (non-suffixed `...Version`) lines. + - Revert changes to entries that have no 8.0 update: `Microsoft.Bcl.Memory`, `System.Numerics.Tensors`, `System.Memory.Data`. + - Then `git add eng/Versions.props`. + +4. Commit: + + ``` + git commit -m "Update 8.0 dependencies" + ``` + +Only `NuGet.config` and `eng/Versions.props` belong in this commit. + +## Sub-stage 3 - Update Dependencies: .NET 10 + +Apply each 10.0 BAR build ID (order does not matter): + +``` +darc update-dependencies --id --no-coherency-updates +``` + +Once all IDs are applied, fix up the changes: + +1. Revert `Version.Details.xml`: + + ``` + git checkout eng/Version.Details.xml + ``` + +2. Revert the Arcade / Helix / Build.Tasks.Templating tooling file changes under `eng/common/` (darc rewrites these because Arcade ships in the VMR): + + ``` + git checkout eng/common + ``` + +3. `NuGet.config` -- resolve keeping both, exactly as in Sub-stage 2: keep the pre-existing and the newly added `internal package sources` feeds. Then `git add NuGet.config`. + +4. `eng/Versions.props` hand-edit: + - **Keep** the Arcade `MicrosoftDotNetBuildTasksTemplating*Version` entry updates (both the base entry and the `...Net10Version` variant). These stay -- only the `eng/common/` tooling files and `Version.Details.xml` are reverted for Arcade. + - Move the 10.0 versions darc produced into the matching `...Net10Version` entries, replacing their previous values ("Version>" becomes "Net10Version>"). + - Revert the changes to the 9.0 (non-suffixed `...Version`) lines. + - Then `git add eng/Versions.props`. + +5. Commit: + + ``` + git commit -m "Update 10.0 dependencies" + ``` + +Only `NuGet.config` and `eng/Versions.props` belong in this commit -- no `eng/common/` files and no `Version.Details.xml`. + +## After the stage + +Do not push from within this stage. Pushing the `stage-release-.` branch is a separate, user-directed step. When it happens, resolve the internal remote by its URL (`/internal/_git/dotnet-extensions`) rather than assuming a remote name, which varies between clones. From 15d9aa7ded1a6fbc4c531b5c0c423c4b24503c33 Mon Sep 17 00:00:00 2001 From: Jeff Handley Date: Tue, 14 Jul 2026 03:07:34 -0700 Subject: [PATCH 03/15] Add Stage 3 - Validate and Land to prepare-release skill Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: f88e403f-7492-48d8-bb79-d987bd99ddc5 --- .github/skills/prepare-release/SKILL.md | 8 +- .../references/stage-3-validate-and-land.md | 76 +++++++++++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 .github/skills/prepare-release/references/stage-3-validate-and-land.md diff --git a/.github/skills/prepare-release/SKILL.md b/.github/skills/prepare-release/SKILL.md index df9f755c03b..9895630506e 100644 --- a/.github/skills/prepare-release/SKILL.md +++ b/.github/skills/prepare-release/SKILL.md @@ -18,7 +18,7 @@ Complete stages strictly in order. Treat each stage -- and each sub-stage of a s 3. Wait for the user's approval before committing, unless the stage's reference file directs automatic commits. 4. Create a single commit that contains only that stage's (or sub-stage's) changes. -Every stage gets its own commit, and every sub-stage gets its own commit. Never combine multiple stages or sub-stages into a single commit. Never push until the user explicitly instructs it, whatever a stage's commit cadence. +Every stage gets its own commit, and every sub-stage gets its own commit. Never combine multiple stages or sub-stages into a single commit. Never push until the user explicitly instructs it, whatever a stage's commit cadence. Stage 3 is the exception: it is operational -- it pushes existing commits and queues pipelines rather than producing a commit of its own. ## Stage 1 - Prepare Internal Branch @@ -31,3 +31,9 @@ Read and follow [references/stage-1-prepare-internal-branch.md](references/stage Update the branch's product dependencies to the pending .NET 9, .NET 8, and .NET 10 servicing releases using `darc update-dependencies`. The BAR build IDs come from the release.dot.net Release Tracker, which is behind Microsoft auth and unreachable by the agent, so the user supplies them (pasted `ReleaseManifest.json` or a downloaded copy). This stage has three sub-stages, each its own commit: .NET 9, then .NET 8, then .NET 10. Read and follow [references/stage-2-update-dependencies.md](references/stage-2-update-dependencies.md). + +## Stage 3 - Validate and Land + +Validate the prepared `stage-release-.` branch with an official build, then land it on the `internal/release/.` branch. This stage is operational -- it pushes existing commits and queues the `official-build-pipeline` pipeline rather than creating new commits -- so it runs only on explicit user instruction and pauses before every push and land action. It gates landing on a green stage-branch pipeline and never auto-completes a pull request that needs elevation. + +Read and follow [references/stage-3-validate-and-land.md](references/stage-3-validate-and-land.md). diff --git a/.github/skills/prepare-release/references/stage-3-validate-and-land.md b/.github/skills/prepare-release/references/stage-3-validate-and-land.md new file mode 100644 index 00000000000..b34058491aa --- /dev/null +++ b/.github/skills/prepare-release/references/stage-3-validate-and-land.md @@ -0,0 +1,76 @@ +# Stage 3 - Validate and Land + +After Stages 1 and 2 are committed and reviewed, validate the `stage-release-.` branch with an official build and land it on the `internal/release/.` branch. + +This stage is operational, not file-editing: it pushes existing commits and queues pipelines rather than producing new commits. Because it pushes to the internal remote and can land changes irreversibly, it runs **only on explicit user instruction** and pauses for confirmation before every push, pipeline queue, and land action. This overrides the commit-per-stage cadence in `SKILL.md` -- Stage 3 produces no commit of its own. + +## Prerequisites + +- Stages 1 and 2 are complete and the working tree is clean on `stage-release-.`. + # run the official build pipeline on the branch and confirm it succeeds +- You have push access to the internal release repository. + +## Resolve the internal remote + +The internal remote is the one whose URL is ``. Resolve it by URL rather than by name -- the remote name varies between clones: + +``` +git remote -v +``` + +Use the remote name that maps to that URL (referred to below as ``). + +## Sub-stage 1 - Validate the stage branch + +Do this only when the user instructs you to push. + +1. Push the stage branch to the internal remote: + + ``` + git push stage-release-. + ``` + +2. Queue the official build pipeline against the stage branch: + + ``` + # run the official build pipeline on the branch and confirm it succeeds + ``` + +3. Poll the run until it completes and confirm the result is `succeeded`: + + ``` + # run the official build pipeline on the branch and confirm it succeeds + ``` + + Do not land the branch until this run is confirmed green. If it fails, investigate, fix on the stage branch (each fix is a fresh commit), and re-run. + +## Sub-stage 2 - Land on the internal release branch + +Land only after the stage-branch pipeline is confirmed successful, and only when the user has chosen an option and confirmed. There are two options. + +### Option 1 - Direct push (bypasses PR review) + +Only valid once the stage-branch pipeline is green -- that run is the only readiness gate: + +``` +git push stage-release-.:internal/release/. +``` + +### Option 2 - Internal pull request + +- Create a pull request from `stage-release-.` targeting `internal/release/.` at ``. +- Complete it with a SQUASH commit titled `Prepare . release`, with the detailed commit message cleared. + +Never auto-complete a pull request that requires JIT elevation or repository admin-setting changes; hand those actions to the user. + +## Sub-stage 3 - Run the official build + +After the changes land on `internal/release/.`, queue `official-build-pipeline` against that branch to produce the official release build whose artifacts will be published: + +``` + # run the official build pipeline on the branch and confirm it succeeds +``` + +## After the stage + +Stage 3 produces no repository commit. Once the official build on `internal/release/.` succeeds, the branch is ready for the release-publication steps (publishing packages, channel promotion, release notes), which are outside this skill. From 1273a3ac7d0e354d8c2d54a012b6e05b69e44a04 Mon Sep 17 00:00:00 2001 From: Jeff Handley Date: Tue, 14 Jul 2026 15:45:17 -0700 Subject: [PATCH 04/15] Add Stage 4 - Publish and Verify to prepare-release skill Includes Test-SourceLink.ps1 for automated Source Link / symbol-server validation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: f88e403f-7492-48d8-bb79-d987bd99ddc5 --- .github/skills/prepare-release/SKILL.md | 8 ++- .../references/stage-4-publish-and-verify.md | 36 ++++++++++++ .../scripts/Test-SourceLink.ps1 | 58 +++++++++++++++++++ 3 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 .github/skills/prepare-release/references/stage-4-publish-and-verify.md create mode 100644 .github/skills/prepare-release/scripts/Test-SourceLink.ps1 diff --git a/.github/skills/prepare-release/SKILL.md b/.github/skills/prepare-release/SKILL.md index 9895630506e..ef769be7fe8 100644 --- a/.github/skills/prepare-release/SKILL.md +++ b/.github/skills/prepare-release/SKILL.md @@ -18,7 +18,7 @@ Complete stages strictly in order. Treat each stage -- and each sub-stage of a s 3. Wait for the user's approval before committing, unless the stage's reference file directs automatic commits. 4. Create a single commit that contains only that stage's (or sub-stage's) changes. -Every stage gets its own commit, and every sub-stage gets its own commit. Never combine multiple stages or sub-stages into a single commit. Never push until the user explicitly instructs it, whatever a stage's commit cadence. Stage 3 is the exception: it is operational -- it pushes existing commits and queues pipelines rather than producing a commit of its own. +Every stage gets its own commit, and every sub-stage gets its own commit. Never combine multiple stages or sub-stages into a single commit. Never push until the user explicitly instructs it, whatever a stage's commit cadence. Stages 3 and 4 are the exception: they are operational -- they push commits, queue pipelines, and publish/verify packages rather than producing a commit of their own. ## Stage 1 - Prepare Internal Branch @@ -37,3 +37,9 @@ Read and follow [references/stage-2-update-dependencies.md](references/stage-2-u Validate the prepared `stage-release-.` branch with an official build, then land it on the `internal/release/.` branch. This stage is operational -- it pushes existing commits and queues the `official-build-pipeline` pipeline rather than creating new commits -- so it runs only on explicit user instruction and pauses before every push and land action. It gates landing on a green stage-branch pipeline and never auto-completes a pull request that needs elevation. Read and follow [references/stage-3-validate-and-land.md](references/stage-3-validate-and-land.md). + +## Stage 4 - Publish and Verify + +After Stage 3 lands the release and its official build produces the package artifacts, publish the packages to nuget.org and verify Source Link on the shipped symbols. Like Stage 3 this stage is operational and produces no commit; publishing is human-gated and irreversible, so the agent stages and reviews the package set but never runs the push or handles API keys, while the Source Link verification (via `scripts/Test-SourceLink.ps1`) is automated. + +Read and follow [references/stage-4-publish-and-verify.md](references/stage-4-publish-and-verify.md). diff --git a/.github/skills/prepare-release/references/stage-4-publish-and-verify.md b/.github/skills/prepare-release/references/stage-4-publish-and-verify.md new file mode 100644 index 00000000000..4fcad3454fd --- /dev/null +++ b/.github/skills/prepare-release/references/stage-4-publish-and-verify.md @@ -0,0 +1,36 @@ +# Stage 4 - Publish and Verify + +After Stage 3 lands the release on `internal/release/.` and its official build produces the `PackageArtifacts`, publish the packages to nuget.org and verify Source Link on the shipped symbols. + +Like Stage 3, this stage is operational and produces no commit. Publishing is **human-gated and irreversible**, so the agent prepares and reviews the package set but never runs `dotnet nuget push` itself and never handles API keys. The Source Link verification is fully automated. + +## Prerequisites + +- Stage 3 complete: the official build on `internal/release/.` succeeded and produced the `PackageArtifacts`. +- For verification: the `sourcelink` and `dotnet-symbol` global tools (`dotnet tool install -g sourcelink`; `dotnet tool install -g dotnet-symbol`). + +## Sub-stage 1 - Prepare and publish packages + +Publishing is irreversible (a published version cannot be overwritten or truly deleted) and requires nuget.org API keys, so the agent only prepares the set; the user runs the push. + +1. Download and extract the `PackageArtifacts` from the official build. +2. Stage the packages to publish into a clean folder, **excluding**: + - `Microsoft.Internal.*` (always). + - Any packages the user names to hold back for this release. Confirm the exact list, and flag template/tooling packages (for example `*.ProjectTemplates`) for an explicit decision. +3. Present the excluded and to-publish lists for the user to review. +4. Two nuget.org accounts are involved: almost all packages publish from the **dotnetframework** account; **`Microsoft.Agents.AI.ProjectTemplates`** publishes from the **MicrosoftAgentFramework** account, so it must be pushed separately with that account's key. +5. The **user** runs `dotnet nuget push` with the appropriate API key(s). Never run the push, and never handle the API keys. + +## Sub-stage 2 - Verify Source Link and symbols + +Once the packages and their symbols are published (symbol publishing happens via the channel-promotion step and lags the nuget push), verify Source Link and symbol-server availability: + +``` +scripts/Test-SourceLink.ps1 -PackageDir +``` + +Each package reports `valid`, `sourcelink-FAILED`, `symbols-not-indexed`, or `no-lib-dll` (template/tooling package). Symbol indexing on nuget.org lags publishing, so `symbols-not-indexed` right after publish is expected -- **re-run until every library package is `valid`**. Investigate any `sourcelink-FAILED`. + +## After the stage + +Stage 4 produces no repository commit. The remaining release activities (dotnet-public mirror, release notes and tag, the internal-to-public and release-to-main merges, and the support-page update) are outside the scope of these stages. diff --git a/.github/skills/prepare-release/scripts/Test-SourceLink.ps1 b/.github/skills/prepare-release/scripts/Test-SourceLink.ps1 new file mode 100644 index 00000000000..39645999c6e --- /dev/null +++ b/.github/skills/prepare-release/scripts/Test-SourceLink.ps1 @@ -0,0 +1,58 @@ +<# +.SYNOPSIS + Validates Source Link + symbol-server availability for published NuGet packages, + the same two things nuget.info ("Valid with Symbol Server") checks. + +.DESCRIPTION + For each .nupkg: extracts a lib DLL, pulls the matching PDB from the NuGet symbol + server via dotnet-symbol, then runs `sourcelink test` on the PDB. + + Results: + valid Source Link resolved and symbols were on the server + sourcelink-FAILED Symbols found, but Source Link did not validate + symbols-not-indexed PDB not yet on the symbol server (still "Validating..." on nuget.info) + no-lib-dll Package has no lib/**/*.dll (template/tooling package) + + Requires: dotnet tool install -g sourcelink ; dotnet tool install -g dotnet-symbol + +.EXAMPLE + ./Test-SourceLink.ps1 -PackageDir C:\path\to\published\packages +#> +param( + [string]$PackageDir = '.', + [string]$SymbolServer = 'https://symbols.nuget.org/download/symbols/', + [int]$Max = 0 # 0 = all packages +) + +$ErrorActionPreference = 'Continue' +$env:PATH += ';' + (Join-Path $env:USERPROFILE '.dotnet\tools') + +$tmp = Join-Path $env:TEMP ('srclink-' + [guid]::NewGuid().ToString('N')) +New-Item -ItemType Directory -Path $tmp | Out-Null + +$pkgs = Get-ChildItem -Path $PackageDir -Filter '*.nupkg' +if ($Max -gt 0) { $pkgs = $pkgs | Select-Object -First $Max } + +$results = foreach ($pkg in $pkgs) { + $ed = Join-Path $tmp ([IO.Path]::GetFileNameWithoutExtension($pkg.Name)) + Expand-Archive -Path $pkg.FullName -DestinationPath $ed -Force + + $dll = Get-ChildItem -Path (Join-Path $ed 'lib') -Filter '*.dll' -Recurse -ErrorAction SilentlyContinue | + Sort-Object { if ($_.Directory.Name -eq 'net8.0') { 0 } else { 1 } } | Select-Object -First 1 + if (-not $dll) { [pscustomobject]@{ Package = $pkg.Name; Result = 'no-lib-dll' }; continue } + + $sd = Join-Path $ed 'sym'; New-Item -ItemType Directory -Path $sd | Out-Null + Copy-Item $dll.FullName $sd + dotnet-symbol --symbols --server-path $SymbolServer --output $sd (Join-Path $sd $dll.Name) 2>&1 | Out-Null + $pdb = Get-ChildItem -Path $sd -Filter '*.pdb' | Select-Object -First 1 + if (-not $pdb) { [pscustomobject]@{ Package = $pkg.Name; Result = 'symbols-not-indexed' }; continue } + + sourcelink test $pdb.FullName 2>&1 | Out-Null + $res = if ($LASTEXITCODE -eq 0) { 'valid' } else { 'sourcelink-FAILED' } + [pscustomobject]@{ Package = $pkg.Name; Result = $res } +} + +$results | Sort-Object Result, Package | Format-Table -AutoSize +Write-Output '' +$results | Group-Object Result | ForEach-Object { "{0,-22} {1}" -f $_.Name, $_.Count } +Remove-Item $tmp -Recurse -Force -ErrorAction SilentlyContinue From a87a8d02554f8ba07307802b692575ed1be2b8f5 Mon Sep 17 00:00:00 2001 From: Jeff Handley Date: Tue, 14 Jul 2026 16:00:16 -0700 Subject: [PATCH 05/15] Add Stage 5 - Reconcile Branches to prepare-release skill Automates staging the internal-to-public and release-to-main merges (with the infra-file doctoring and stabilization revert); pushing and PR completion remain user-directed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: f88e403f-7492-48d8-bb79-d987bd99ddc5 --- .github/skills/prepare-release/SKILL.md | 8 +- .../references/stage-4-publish-and-verify.md | 2 +- .../references/stage-5-reconcile-branches.md | 79 +++++++++++++++++++ 3 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 .github/skills/prepare-release/references/stage-5-reconcile-branches.md diff --git a/.github/skills/prepare-release/SKILL.md b/.github/skills/prepare-release/SKILL.md index ef769be7fe8..cd6335f9724 100644 --- a/.github/skills/prepare-release/SKILL.md +++ b/.github/skills/prepare-release/SKILL.md @@ -18,7 +18,7 @@ Complete stages strictly in order. Treat each stage -- and each sub-stage of a s 3. Wait for the user's approval before committing, unless the stage's reference file directs automatic commits. 4. Create a single commit that contains only that stage's (or sub-stage's) changes. -Every stage gets its own commit, and every sub-stage gets its own commit. Never combine multiple stages or sub-stages into a single commit. Never push until the user explicitly instructs it, whatever a stage's commit cadence. Stages 3 and 4 are the exception: they are operational -- they push commits, queue pipelines, and publish/verify packages rather than producing a commit of their own. +Every stage gets its own commit, and every sub-stage gets its own commit. Never combine multiple stages or sub-stages into a single commit. Never push until the user explicitly instructs it, whatever a stage's commit cadence. Stages 3, 4, and 5 are the exception: they are operational -- they push commits, queue pipelines, publish/verify packages, and stage merge commits rather than producing an ordinary stage commit. ## Stage 1 - Prepare Internal Branch @@ -43,3 +43,9 @@ Read and follow [references/stage-3-validate-and-land.md](references/stage-3-val After Stage 3 lands the release and its official build produces the package artifacts, publish the packages to nuget.org and verify Source Link on the shipped symbols. Like Stage 3 this stage is operational and produces no commit; publishing is human-gated and irreversible, so the agent stages and reviews the package set but never runs the push or handles API keys, while the Source Link verification (via `scripts/Test-SourceLink.ps1`) is automated. Read and follow [references/stage-4-publish-and-verify.md](references/stage-4-publish-and-verify.md). + +## Stage 5 - Reconcile Branches + +After Stage 4 publishes the release, merge the internal release branch back out to the public `release/.` branch (discarding the internal-only infrastructure changes) and then merge that public branch into `main` (reverting the stable-versioning flip). Like Stages 3 and 4 it is operational: the agent stages each merge, applies the required file doctoring, commits on a `merge/*` branch, and shows the diff -- but pushing and completing the merge-commit PRs (which need elevation) are user-directed. + +Read and follow [references/stage-5-reconcile-branches.md](references/stage-5-reconcile-branches.md). diff --git a/.github/skills/prepare-release/references/stage-4-publish-and-verify.md b/.github/skills/prepare-release/references/stage-4-publish-and-verify.md index 4fcad3454fd..7ed1fe61b92 100644 --- a/.github/skills/prepare-release/references/stage-4-publish-and-verify.md +++ b/.github/skills/prepare-release/references/stage-4-publish-and-verify.md @@ -33,4 +33,4 @@ Each package reports `valid`, `sourcelink-FAILED`, `symbols-not-indexed`, or `no ## After the stage -Stage 4 produces no repository commit. The remaining release activities (dotnet-public mirror, release notes and tag, the internal-to-public and release-to-main merges, and the support-page update) are outside the scope of these stages. +Stage 4 produces no repository commit. The internal-to-public and release-to-main merges are handled by Stage 5. The remaining activities (dotnet-public mirror, release notes and tag, and the support-page update) are outside the scope of these stages. diff --git a/.github/skills/prepare-release/references/stage-5-reconcile-branches.md b/.github/skills/prepare-release/references/stage-5-reconcile-branches.md new file mode 100644 index 00000000000..5932dc3fd45 --- /dev/null +++ b/.github/skills/prepare-release/references/stage-5-reconcile-branches.md @@ -0,0 +1,79 @@ +# Stage 5 - Reconcile Branches + +After Stage 4 publishes and verifies the release, reconcile the branches: merge the internal release branch back out to the public release branch, then merge the public release branch into `main`. This is a post-publish activity. + +Like Stages 3 and 4, this stage is operational: it stages merges and creates commits on throwaway `merge/*` branches, but it does **not** push or complete pull requests on its own. The agent stages each merge, applies the required file "doctoring", commits, and shows the diff for review; **pushing and completing the PRs (which need JIT elevation / admin settings) are user-directed**, and each merge PR must land as a merge commit (never squashed). + +Run each sub-stage only when the user instructs it. Sub-stage 2 depends on Sub-stage 1's PR having already merged into the public release branch. + +## Prerequisites + +- Stage 4 complete: packages published and the release is public. +- A clone with the public GitHub remote (referred to here as `dotnet`) and up-to-date `internal/release/.`, `release/.`, and `main` branches. Fetch all three first. + +## Sub-stage 1 - Merge internal release branch into the public release branch + +Carry the version bumps and internal product changes out to the public `release/.` branch while discarding the internal-only infrastructure changes (the Stage 1 prep). + +1. From `release/.`, create the merge branch (do not use a `release/` prefix -- it is protected): + + ``` + git switch release/. + git switch -c merge/. + ``` + +2. Stage the merge without committing: + + ``` + git merge --no-ff --no-commit internal/release/. + ``` + +3. Discard the internal-only infrastructure changes so they do not reach the public branch -- restore these to the public release-branch version: + + ``` + git checkout HEAD -- Directory.Build.props NuGet.config azure-pipelines.yml eng/pipelines/templates/BuildAndTest.yml + ``` + +4. Keep everything else staged, in particular the versioning changes (`eng/Version.Details.xml`, `eng/Versions.props`) and any internal-only product changes / backports. + +5. Review the staged diff: it should be version bumps + product changes, with **no** infrastructure changes. Then commit: + + ``` + git commit -m "Merge published release into release/." + ``` + +6. Pushing (user-directed) to `dotnet` and opening the PR into `release/.` -- title "Merge published release into release/.", label `DO-NOT-SQUASH`, description "Merge using a merge commit. Do not squash." Do not enable auto-merge (squash). Completing the PR (JIT elevation, allow merge commits) is done by the user. + +## Sub-stage 2 - Merge the public release branch into main + +Do this only after Sub-stage 1's PR has merged into `release/.`. It reverts the stable-versioning flip so `main` does not produce stable/release versions. + +1. Fetch, then from `main` create the merge branch: + + ``` + git switch main + git switch -c merge/.-to-main + ``` + +2. Stage the merge without committing: + + ``` + git merge --no-ff --no-commit release/. + ``` + +3. In `eng/Versions.props`, revert only the package-stabilization change (keep all version numbers and all `eng/Version.Details.xml` changes): + - Set `StabilizePackageVersion` back to `false`. + - Set `DotNetFinalVersionKind` back to empty (``). + - `git add eng/Versions.props`. + +4. Review the staged diff, then commit: + + ``` + git commit -m "Merge release/. into main" + ``` + +5. Pushing (user-directed) to `dotnet` and opening the PR into `main` -- title "Merge release/. into main", label `DO-NOT-SQUASH`, description "Merge using a merge commit. Do not squash." Do not enable auto-merge (squash). The user completes the PR. + +## After the stage + +Stage 5 produces merge commits on `merge/*` branches but does not push or complete the pull requests. The remaining release activities (dotnet-public mirror, release notes and tag, and the support-page update) are outside the scope of these stages. From efc192a6e27af479cbeb69e4ae943c927e674332 Mon Sep 17 00:00:00 2001 From: Jeff Handley Date: Tue, 14 Jul 2026 17:34:03 -0700 Subject: [PATCH 06/15] Teach Stage 5 to compare the internal-to-public merge against the last 5 releases Adds an anomaly-vs-consistency check (versioning present, infra absent, skill/tooling excluded, expected backports) against the previous five releases' corresponding merge PRs before committing. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: f88e403f-7492-48d8-bb79-d987bd99ddc5 --- .../references/stage-5-reconcile-branches.md | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/.github/skills/prepare-release/references/stage-5-reconcile-branches.md b/.github/skills/prepare-release/references/stage-5-reconcile-branches.md index 5932dc3fd45..3870b3169a4 100644 --- a/.github/skills/prepare-release/references/stage-5-reconcile-branches.md +++ b/.github/skills/prepare-release/references/stage-5-reconcile-branches.md @@ -36,13 +36,26 @@ Carry the version bumps and internal product changes out to the public `release/ 4. Keep everything else staged, in particular the versioning changes (`eng/Version.Details.xml`, `eng/Versions.props`) and any internal-only product changes / backports. -5. Review the staged diff: it should be version bumps + product changes, with **no** infrastructure changes. Then commit: +5. Compare against the last five releases to check for anomalies vs. consistency. Look at the corresponding merge PRs from the previous five monthly releases (their titles vary -- e.g. "Merge published release into release/X.Y", "Merging internal changes into the release/X.Y branch", "Merge internal changes from X.Y"). Find them and list their changed files with `gh`: + + ``` + gh pr list --repo dotnet/extensions --base release/. --state merged --json number,title + gh pr view --repo dotnet/extensions --json files + ``` + + Compare this merge's staged file set (`git diff --cached --stat`) against them and flag anomalies to the user before committing: + - `eng/Version.Details.xml` and `eng/Versions.props` appear in every release and must be here too (including the stabilization flip). + - The internal-only infrastructure files (`Directory.Build.props`, `NuGet.config`, `azure-pipelines.yml`, `eng/pipelines/templates/BuildAndTest.yml`) are absent in every recent release and must be absent here too. + - `.github/skills/**` and other tooling files are excluded by recent releases -- flag if present. + - The remaining files should be version bumps plus intentional product/test backports. Flag anything unexpected: an unusually large or empty change set, missing versioning, leaked infrastructure, or files no prior release touched. + +6. Review the staged diff: it should be version bumps + product changes, with **no** infrastructure changes. Then commit: ``` git commit -m "Merge published release into release/." ``` -6. Pushing (user-directed) to `dotnet` and opening the PR into `release/.` -- title "Merge published release into release/.", label `DO-NOT-SQUASH`, description "Merge using a merge commit. Do not squash." Do not enable auto-merge (squash). Completing the PR (JIT elevation, allow merge commits) is done by the user. +7. Pushing (user-directed) to `dotnet` and opening the PR into `release/.` -- title "Merge published release into release/.", label `DO-NOT-SQUASH`, description "Merge using a merge commit. Do not squash." Do not enable auto-merge (squash). Completing the PR (JIT elevation, allow merge commits) is done by the user. ## Sub-stage 2 - Merge the public release branch into main From 29eec0f0597d1e547508d5dda09ccfce2b5eab1e Mon Sep 17 00:00:00 2001 From: Jeff Handley Date: Tue, 14 Jul 2026 19:06:38 -0700 Subject: [PATCH 07/15] Note that prepare-release commits omit the Copilot-Session trailer Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/skills/prepare-release/SKILL.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/skills/prepare-release/SKILL.md b/.github/skills/prepare-release/SKILL.md index cd6335f9724..03d3e6d5a59 100644 --- a/.github/skills/prepare-release/SKILL.md +++ b/.github/skills/prepare-release/SKILL.md @@ -20,6 +20,8 @@ Complete stages strictly in order. Treat each stage -- and each sub-stage of a s Every stage gets its own commit, and every sub-stage gets its own commit. Never combine multiple stages or sub-stages into a single commit. Never push until the user explicitly instructs it, whatever a stage's commit cadence. Stages 3, 4, and 5 are the exception: they are operational -- they push commits, queue pipelines, publish/verify packages, and stage merge commits rather than producing an ordinary stage commit. +Commits this skill creates land in public dotnet/extensions history -- the stage commits flow out through the non-squash internal-to-public merge, and Stage 5's reconciliation merge commits land directly. Keep the `Co-authored-by: Copilot` trailer on those commits but omit the `Copilot-Session` trailer. + ## Stage 1 - Prepare Internal Branch Apply the internal-release infrastructure changes to the branch: suppress `NU1507`, remove the NuGet package source mapping, switch on stable/release versioning, add private-feed credential setup to the build template, comment out integration tests, and remove the code-coverage pipeline stage. Never change version numbers here -- those flow via Dependency Flow automation. From 7c7d2fe03f0c2626ed2bcce1c010618da4e108ec Mon Sep 17 00:00:00 2001 From: Jeff Handley Date: Tue, 14 Jul 2026 19:12:31 -0700 Subject: [PATCH 08/15] Make internal-landing Option 2 non-squash (rebase and fast-forward) to match Option 1 Preserves each sub-stage commit as linear history for auditability, consistent with the direct-push option. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../prepare-release/references/stage-3-validate-and-land.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/skills/prepare-release/references/stage-3-validate-and-land.md b/.github/skills/prepare-release/references/stage-3-validate-and-land.md index b34058491aa..3547dd24376 100644 --- a/.github/skills/prepare-release/references/stage-3-validate-and-land.md +++ b/.github/skills/prepare-release/references/stage-3-validate-and-land.md @@ -59,7 +59,7 @@ git push stage-release-.:internal/release/.` targeting `internal/release/.` at ``. -- Complete it with a SQUASH commit titled `Prepare . release`, with the detailed commit message cleared. +- Complete it with **Rebase and fast-forward** (not Squash), preserving each sub-stage commit as a linear history so it matches Option 1's direct push and keeps each sub-stage's actions auditable. Do not squash. Never auto-complete a pull request that requires JIT elevation or repository admin-setting changes; hand those actions to the user. From 0557d78f9da388606809840365ef16548a7db33d Mon Sep 17 00:00:00 2001 From: Jeff Handley Date: Wed, 15 Jul 2026 11:04:49 -0700 Subject: [PATCH 09/15] Fix Test-SourceLink.ps1 to query the Microsoft symbol server (msdl) dotnet/extensions publishes symbols to msdl, not symbols.nuget.org; the script now queries --microsoft-symbol-server (with the nuget server as a fallback), so it no longer reports false symbols-not-indexed for valid releases. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/skills/prepare-release/scripts/Test-SourceLink.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/skills/prepare-release/scripts/Test-SourceLink.ps1 b/.github/skills/prepare-release/scripts/Test-SourceLink.ps1 index 39645999c6e..68ad8ec3d06 100644 --- a/.github/skills/prepare-release/scripts/Test-SourceLink.ps1 +++ b/.github/skills/prepare-release/scripts/Test-SourceLink.ps1 @@ -4,8 +4,8 @@ the same two things nuget.info ("Valid with Symbol Server") checks. .DESCRIPTION - For each .nupkg: extracts a lib DLL, pulls the matching PDB from the NuGet symbol - server via dotnet-symbol, then runs `sourcelink test` on the PDB. + For each .nupkg: extracts a lib DLL, pulls the matching PDB from the Microsoft symbol + server (msdl) via dotnet-symbol, then runs `sourcelink test` on the PDB. Results: valid Source Link resolved and symbols were on the server @@ -43,7 +43,7 @@ $results = foreach ($pkg in $pkgs) { $sd = Join-Path $ed 'sym'; New-Item -ItemType Directory -Path $sd | Out-Null Copy-Item $dll.FullName $sd - dotnet-symbol --symbols --server-path $SymbolServer --output $sd (Join-Path $sd $dll.Name) 2>&1 | Out-Null + dotnet-symbol --symbols --microsoft-symbol-server --server-path $SymbolServer --output $sd (Join-Path $sd $dll.Name) 2>&1 | Out-Null $pdb = Get-ChildItem -Path $sd -Filter '*.pdb' | Select-Object -First 1 if (-not $pdb) { [pscustomobject]@{ Package = $pkg.Name; Result = 'symbols-not-indexed' }; continue } From 9c35f14d6d10e290ef693243940e641d77a8fa2a Mon Sep 17 00:00:00 2001 From: Jeff Handley Date: Wed, 15 Jul 2026 11:11:15 -0700 Subject: [PATCH 10/15] write-release-notes: dedup backported PRs by harvesting referenced main-PR numbers Add a dedup step that lists every PR merged into the prior release branch and harvests the #NNNN references from each PR's title and body, excluding candidates whose number appears in that set. This covers single-PR backports (e.g. #7547 -> #7546) and aggregate backports that carry many main PRs with no title twin (e.g. #7402 -> thirteen PRs). Keep title equivalence as a fallback for backports that omit the reference, and require the ancestry, notes-body, reference-harvest, and title checks to all run before marking candidates. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../write-release-notes/references/collect-prs.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/skills/write-release-notes/references/collect-prs.md b/.github/skills/write-release-notes/references/collect-prs.md index de21ba88d89..7b3419990fb 100644 --- a/.github/skills/write-release-notes/references/collect-prs.md +++ b/.github/skills/write-release-notes/references/collect-prs.md @@ -133,10 +133,18 @@ For each candidate PR, check whether it was already included in any prior releas 1. **Check against the prior release tag**: `git merge-base --is-ancestor `. If the PR's merge commit is an ancestor of the previous release tag, it shipped in that release — exclude it. 2. **Check against the prior release branch HEAD**: The release branch may have advanced beyond the release tag (e.g. `release/10.3` may contain commits merged after `v10.3.0` was tagged but before `v10.3.1` or the branch was abandoned). Check: `git merge-base --is-ancestor /release/10.3`. If reachable, the PR was part of that release branch's content — exclude it. 3. **Check the prior release notes body**: Fetch the GitHub release for the previous tag and check if the PR number appears in the release notes body. This catches PRs that were explicitly covered. +4. **Harvest `main`-PR references from every PR merged into the prior release branch**: Backport PRs name the `main` PR number(s) they carry -- in the PR **title** (a trailing `(#NNNN)`) and/or **body** (`Backport of #NNNN`, or a checklist/list of numbers). This is the reliable signal, and it handles both shapes of backport: + - **Single-PR backport**: `[release/10.7] Fix ToolJson.AdditionalProperties to accept sub-schema objects` (#7547) references its `main` twin #7546 in the body. + - **Aggregate backport**: `Stage an MEAI 10.4.1 release` (#7402) references **thirteen** `main` PRs in its body (`#7285, #7340, #7369, #7373, #7379, #7381, #7382, #7387, #7389, #7394, #7398, #7399, #7400`) and has **no `[release/...]` title twin at all**. Title-matching would miss every one of them. -> **Why this matters:** A PR can be merged into a `release/` branch, ship in that release's packages, but never appear in that release's notes (e.g. a late-breaking fix). When that PR is later merged into `main`, it appears in the date-range search for the next release. Without branch-aware deduplication, it would be incorrectly included in the new release notes. + Build a "shipped-via-backport" set: list every PR merged into the prior release branch (`gh pr list --repo dotnet/extensions --base release/. --state merged --limit 200 --json number,title,body`), then extract every `#NNNN` reference from each PR's **title and body**. Exclude any candidate whose PR number appears in that set. These are `main` PRs whose changes shipped in the prior release even though the `main` PR's own merge commit is new to the current release's lineage, so the ancestry checks in 1-2 miss them. +5. **Fallback -- title equivalence**: For the rare backport that omits the `(#NNNN)` reference, fall back to matching titles. Normalize each candidate's title (strip any leading `[release/x.y] ` prefix and any trailing `(#NNNN)`), then look for a PR merged into the prior release branch with a matching normalized title (`gh pr list --repo dotnet/extensions --base release/. --state merged --search " in:title"`), or a title match against the prior release notes body. Treat a match as an exclusion. -This step is critical and must run before marking PRs as candidates. +> **Why this matters:** A change already shipped in the prior release can reappear in the next release's date-range search in two ways: +> - A PR merged directly into a `release/` branch ships in that release but may never appear in its notes; when the branch later merges into `main` it surfaces in the next release's search. The ancestry checks (1-2) catch this. +> - A change is **backported**: it merges into `main` as its own PR (new to the next release's lineage, so ancestry misses it) and separately ships in the prior release via a backport PR merged into that release branch. The backport PR names the `main` PR number(s) it carries -- **one** number for a single-PR backport, **many** for an aggregate backport -- so harvesting those references (4) is what excludes the `main` half. The notes-body number check (3) and title fallback (5) are backups for the rare backport that doesn't cite its source PR numbers. + +Steps 1-5 are critical and must **all** run before marking PRs as candidates. The ancestry checks (1-2) alone will let the `main` half of a backport slip through -- e.g. #7546 when its backport #7547 already shipped in 10.7, or any of the thirteen `main` PRs carried by an aggregate backport like #7402. Harvesting `main`-PR references (4) is the primary defense; the notes-body (3) and title (5) checks are fallbacks for backports that don't cite their source PR numbers. ## Exclusion filters From e5a1827c12392aa869b441686b25604a2a479330 Mon Sep 17 00:00:00 2001 From: Jeff Handley Date: Sun, 19 Jul 2026 03:37:42 -0700 Subject: [PATCH 11/15] Add a release-manager agent for the dotnet/extensions release process Move the release-specific skills out of .github/skills into a repo-local release-manager agent (.github/agents/release-manager) so they load only when the agent is selected rather than always being in scope. The agent orchestrates four playbooks: prepare-release (prepare the internal branch, update .NET dependencies), publish-release (land the branch, publish to nuget.org, promote the shipping build to the public channel so symbols reach msdl), validate-release (verify Source Link and symbols, reconcile the internal/public/main branches, confirm the support-page update), and write-release-notes (draft GitHub release notes for a tag). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/agents/release-manager.agent.md | 68 +++++++++++++++++ .../release-manager/prepare-release/README.md | 34 +++++++++ .../stage-1-prepare-internal-branch.md | 0 .../references/stage-2-update-dependencies.md | 10 +-- .../release-manager/publish-release/README.md | 21 +++++ .../references/stage-1-land.md | 53 +++++++++++++ .../references/stage-2-publish-and-promote.md | 35 +++++++++ .../validate-release/README.md | 27 +++++++ .../references/stage-1-verify-source-link.md | 31 ++++++++ .../references/stage-2-reconcile-branches.md} | 10 +-- .../references/stage-3-support-page.md | 20 +++++ .../scripts/Test-SourceLink.ps1 | 0 .../write-release-notes/README.md} | 11 +-- .../references/categorize-entries.md | 2 +- .../references/collect-prs.md | 0 .../references/editorial-rules.md | 2 +- .../references/experimental-features.md | 0 .../references/format-template.md | 0 .../references/package-areas.md | 0 .../references/sql-storage.md | 0 .github/skills/prepare-release/SKILL.md | 53 ------------- .../references/stage-3-validate-and-land.md | 76 ------------------- .../references/stage-4-publish-and-verify.md | 36 --------- 23 files changed, 303 insertions(+), 186 deletions(-) create mode 100644 .github/agents/release-manager.agent.md create mode 100644 .github/agents/release-manager/prepare-release/README.md rename .github/{skills => agents/release-manager}/prepare-release/references/stage-1-prepare-internal-branch.md (100%) rename .github/{skills => agents/release-manager}/prepare-release/references/stage-2-update-dependencies.md (88%) create mode 100644 .github/agents/release-manager/publish-release/README.md create mode 100644 .github/agents/release-manager/publish-release/references/stage-1-land.md create mode 100644 .github/agents/release-manager/publish-release/references/stage-2-publish-and-promote.md create mode 100644 .github/agents/release-manager/validate-release/README.md create mode 100644 .github/agents/release-manager/validate-release/references/stage-1-verify-source-link.md rename .github/{skills/prepare-release/references/stage-5-reconcile-branches.md => agents/release-manager/validate-release/references/stage-2-reconcile-branches.md} (80%) create mode 100644 .github/agents/release-manager/validate-release/references/stage-3-support-page.md rename .github/{skills/prepare-release => agents/release-manager/validate-release}/scripts/Test-SourceLink.ps1 (100%) rename .github/{skills/write-release-notes/SKILL.md => agents/release-manager/write-release-notes/README.md} (90%) rename .github/{skills => agents/release-manager}/write-release-notes/references/categorize-entries.md (98%) rename .github/{skills => agents/release-manager}/write-release-notes/references/collect-prs.md (100%) rename .github/{skills => agents/release-manager}/write-release-notes/references/editorial-rules.md (99%) rename .github/{skills => agents/release-manager}/write-release-notes/references/experimental-features.md (100%) rename .github/{skills => agents/release-manager}/write-release-notes/references/format-template.md (100%) rename .github/{skills => agents/release-manager}/write-release-notes/references/package-areas.md (100%) rename .github/{skills => agents/release-manager}/write-release-notes/references/sql-storage.md (100%) delete mode 100644 .github/skills/prepare-release/SKILL.md delete mode 100644 .github/skills/prepare-release/references/stage-3-validate-and-land.md delete mode 100644 .github/skills/prepare-release/references/stage-4-publish-and-verify.md diff --git a/.github/agents/release-manager.agent.md b/.github/agents/release-manager.agent.md new file mode 100644 index 00000000000..1e8d3abc005 --- /dev/null +++ b/.github/agents/release-manager.agent.md @@ -0,0 +1,68 @@ +--- +name: release-manager +description: > + Owns the end-to-end dotnet/extensions monthly and servicing release process across four playbooks: + prepare-release (stage the internal branch, update .NET dependencies), publish-release (land the + branch, publish packages to nuget.org, promote the shipping build to the public channel so symbols + reach msdl), validate-release (verify Source Link/symbols on msdl, reconcile the internal/public/main + branches), and write-release-notes (draft GitHub release notes for a tag). + USE FOR: "prepare for a release", "prepare internal release branch", "stage the release", + "update release dependencies", "validate/land the release branch", "publish the release", + "promote the build to the public channel", "fix missing/public symbols for a release", + "reconcile release branches", "write/draft release notes for ", and other dotnet/extensions + release operations. + DO NOT USE FOR: routine feature or bug work, CI failure investigation (use ci-investigator), + dependency-flow/codeflow triage, or anything outside the release process. +--- + +# Release Manager + +You are the release manager for `dotnet/extensions`. You own the monthly and servicing release +process from branch preparation through publishing, symbol availability, branch reconciliation, and +release notes. Pick the right playbook, follow it stage by stage, and keep the human in the loop at +every gate. + +## Playbooks + +Select the playbook that matches the request, read its README first, then load each stage or +reference file **only when you reach it** (progressive disclosure -- do not preload everything). + +| The user wants to... | Playbook | Follow | +|---|---|---| +| Prepare the release content (branch prep, dependency updates) | **prepare-release** | [release-manager/prepare-release/README.md](release-manager/prepare-release/README.md) | +| Ship the release (land, publish to nuget.org, promote to the public channel) | **publish-release** | [release-manager/publish-release/README.md](release-manager/publish-release/README.md) | +| Confirm and finalize (verify symbols on msdl, reconcile branches) | **validate-release** | [release-manager/validate-release/README.md](release-manager/validate-release/README.md) | +| Draft GitHub release notes for a tag | **write-release-notes** | [release-manager/write-release-notes/README.md](release-manager/write-release-notes/README.md) | + +Each playbook keeps its stage references under its own `references/` folder. The Source Link +verification script lives at `release-manager/validate-release/scripts/Test-SourceLink.ps1`. + +## Operating rules (apply to every playbook) + +- **Human-gated and sequential.** Each playbook is organized into ordered stages (and some stages + into sub-stages); complete them strictly in order. Pause for the user to review and approve before + each commit, and before every push, pipeline queue, publish, channel promotion, or merge-commit + completion. **Never push until the user explicitly instructs it.** +- **Irreversibility.** Publishing to nuget.org, promoting a build to a public channel (its symbols + and packages flow to msdl and downstream consumers), and pushing tags cannot be cleanly undone. + Prepare and review first, then act only on explicit user confirmation. Never run `dotnet nuget + push` yourself and never handle nuget.org API keys. +- **Commit hygiene.** Every stage and every sub-stage is its own commit -- never combine them. + Commits that land in public `dotnet/extensions` history keep the `Co-authored-by: Copilot` + trailer but **omit** the `Copilot-Session` trailer. Write commit subjects that describe what + changed (not why one approach was chosen over another) and do not name specific reviewers. +- **Remotes and paths.** The release uses two remotes: the public GitHub remote + (`github.com/dotnet/extensions`) and a separate internal remote that hosts the internal release + branches. Resolve each by its **URL**, not by remote name -- names vary by machine. Do not rely on + absolute on-disk clone paths. +- **Release notes** are never published to a GitHub release without explicit user confirmation. + +## Release process at a glance + +The release runs as three playbooks in order, then release notes: + +1. **prepare-release** -- Stage 1 prepare the internal branch (no version-number edits); Stage 2 update .NET 9, then .NET 8, then .NET 10 dependencies (three sub-stages). Commit-producing preparation. +2. **publish-release** -- Stage 1 land on `internal/release/.` (gated on a green official build); Stage 2 publish to nuget.org and **promote the shipping build to the public `.NET ` channel** (required -- this is what puts symbols on msdl). Operational and irreversible. +3. **validate-release** -- Stage 1 verify Source Link/symbols on msdl until every library package is `valid` (the release sign-off gate); Stage 2 reconcile internal -> public `release/.` -> `main`; Stage 3 confirm the partner team's support-page update. + +Tagging and publishing the GitHub release notes are handled by the **write-release-notes** playbook. diff --git a/.github/agents/release-manager/prepare-release/README.md b/.github/agents/release-manager/prepare-release/README.md new file mode 100644 index 00000000000..7f3cf49084c --- /dev/null +++ b/.github/agents/release-manager/prepare-release/README.md @@ -0,0 +1,34 @@ +# Prepare Release + +Prepares a `dotnet/extensions` public release branch (`release/.`) for the internal release process on the corresponding `internal/release/.` branch. + +The preparation is organized into ordered stages. Work through them in sequence, loading each stage's reference file only when you reach that stage. + +## Stage workflow + +Complete stages strictly in order. Treat each stage -- and each sub-stage of a stage that has them -- as an independent, committable unit. For every stage or sub-stage: + +1. Apply the changes described in the stage's reference file. +2. Prompt the user to review the changes (summarize what changed and show the diff), unless the stage's reference file directs you to commit automatically. +3. Wait for the user's approval before committing, unless the stage's reference file directs automatic commits. +4. Create a single commit that contains only that stage's (or sub-stage's) changes. + +Every stage gets its own commit, and every sub-stage gets its own commit. Never combine multiple stages or sub-stages into a single commit. Never push until the user explicitly instructs it. + +Commits this playbook creates land in public dotnet/extensions history -- they flow out later through the non-squash internal-to-public merge (validate-release, Stage 2). Keep the `Co-authored-by: Copilot` trailer on those commits but omit the `Copilot-Session` trailer. + +## Stage 1 - Prepare Internal Branch + +Apply the internal-release infrastructure changes to the branch: suppress `NU1507`, remove the NuGet package source mapping, switch on stable/release versioning, add private-feed credential setup to the build template, comment out integration tests, and remove the code-coverage pipeline stage. Never change version numbers here -- those flow via Dependency Flow automation. + +Read and follow [references/stage-1-prepare-internal-branch.md](references/stage-1-prepare-internal-branch.md). + +## Stage 2 - Update Dependencies + +Update the branch's product dependencies to the pending .NET 9, .NET 8, and .NET 10 servicing releases using `darc update-dependencies`. The BAR build IDs come from the release.dot.net Release Tracker, which is behind Microsoft auth and unreachable by the agent, so the user supplies them (pasted `ReleaseManifest.json` or a downloaded copy). This stage has three sub-stages, each its own commit: .NET 9, then .NET 8, then .NET 10. + +Read and follow [references/stage-2-update-dependencies.md](references/stage-2-update-dependencies.md). + +## Next + +Preparation is complete once Stages 1-2 are committed and reviewed. The release is then shipped and finalized by the sibling playbooks: **publish-release** (land, publish, promote) and **validate-release** (verify symbols, reconcile branches). diff --git a/.github/skills/prepare-release/references/stage-1-prepare-internal-branch.md b/.github/agents/release-manager/prepare-release/references/stage-1-prepare-internal-branch.md similarity index 100% rename from .github/skills/prepare-release/references/stage-1-prepare-internal-branch.md rename to .github/agents/release-manager/prepare-release/references/stage-1-prepare-internal-branch.md diff --git a/.github/skills/prepare-release/references/stage-2-update-dependencies.md b/.github/agents/release-manager/prepare-release/references/stage-2-update-dependencies.md similarity index 88% rename from .github/skills/prepare-release/references/stage-2-update-dependencies.md rename to .github/agents/release-manager/prepare-release/references/stage-2-update-dependencies.md index 02a6f0dd864..6fc75be1379 100644 --- a/.github/skills/prepare-release/references/stage-2-update-dependencies.md +++ b/.github/agents/release-manager/prepare-release/references/stage-2-update-dependencies.md @@ -29,7 +29,7 @@ The Release Tracker at is a Blazor WebAssembl - **.NET 8** -- the pending 8.0 release's `manifests/ReleaseManifest.json`. - **.NET 10** -- the pending 10.0 release's `manifests/ReleaseManifest.json`, or a pasted list of its BAR build IDs from the release page. -Each build entry in a `ReleaseManifest.json` has a `repo` (like ``) and a `barBuildId`. Read the IDs you need: +Each build entry in a `ReleaseManifest.json` has a `repo` and a `barBuildId`. Read the IDs you need: - **.NET 9 and .NET 8:** the `barBuildId` for `dotnet-runtime`, `dotnet-aspnetcore`, and `dotnet-efcore`. - **.NET 10:** every `barBuildId` (or the pasted list). Order does not matter. @@ -45,7 +45,7 @@ darc update-dependencies --id ``` - `dotnet-efcore` commonly reports `warn: Found no dependencies to update` -- that is expected. -- .NET 9 is the coherent primary update: **keep everything darc changes**, including `eng/Version.Details.xml`, the non-suffixed `...Version` entries in `eng/Versions.props`, and the new `internal package sources` feeds in `NuGet.config`. +- .NET 9 is the coherent primary update: **keep everything darc changes**, including `eng/Version.Details.xml`, the non-suffixed `...Version` entries in `eng/Versions.props`, and the new internal package sources in `NuGet.config`. Review the changes, then stage and commit: @@ -72,7 +72,7 @@ Once all three repos are applied, fix up the changes so that only the 8.0 (`...L git checkout eng/Version.Details.xml ``` -2. `NuGet.config` -- resolve keeping both. Keep the pre-existing `internal package sources` feeds (added by .NET 9) **and** the newly added 8.0 ones; discard the deletions and keep the additions. The result adds the new 8.0 `internal package sources` sources: three in `` and three in ``. Then: +2. `NuGet.config` -- resolve keeping both. Keep the pre-existing internal package sources (added by .NET 9) **and** the newly added 8.0 ones; discard the deletions and keep the additions. The result adds the new 8.0 internal package sources: three in `` and three in ``. Then: ``` git add NuGet.config @@ -114,7 +114,7 @@ Once all IDs are applied, fix up the changes: git checkout eng/common ``` -3. `NuGet.config` -- resolve keeping both, exactly as in Sub-stage 2: keep the pre-existing and the newly added `internal package sources` feeds. Then `git add NuGet.config`. +3. `NuGet.config` -- resolve keeping both, exactly as in Sub-stage 2: keep the pre-existing and the newly added internal package sources. Then `git add NuGet.config`. 4. `eng/Versions.props` hand-edit: - **Keep** the Arcade `MicrosoftDotNetBuildTasksTemplating*Version` entry updates (both the base entry and the `...Net10Version` variant). These stay -- only the `eng/common/` tooling files and `Version.Details.xml` are reverted for Arcade. @@ -132,4 +132,4 @@ Only `NuGet.config` and `eng/Versions.props` belong in this commit -- no `eng/co ## After the stage -Do not push from within this stage. Pushing the `stage-release-.` branch is a separate, user-directed step. When it happens, resolve the internal remote by its URL (`/internal/_git/dotnet-extensions`) rather than assuming a remote name, which varies between clones. +Do not push from within this stage. Pushing the `stage-release-.` branch is a separate, user-directed step. When it happens, resolve the internal remote by its URL rather than assuming a remote name, which varies between clones. diff --git a/.github/agents/release-manager/publish-release/README.md b/.github/agents/release-manager/publish-release/README.md new file mode 100644 index 00000000000..346dac50676 --- /dev/null +++ b/.github/agents/release-manager/publish-release/README.md @@ -0,0 +1,21 @@ +# Publish Release + +Ships a prepared `dotnet/extensions` release: land the prepared branch on `internal/release/.`, then publish the packages to nuget.org and promote the shipping build to the public channel so its symbols reach the Microsoft symbol server (msdl). + +Run this playbook after **prepare-release** is complete. Both stages are **operational** -- they push, queue pipelines, publish, and promote rather than producing ordinary commits -- and every push, publish, and promotion is **irreversible**. Run each stage only on explicit user instruction, and pause for confirmation before every irreversible action. + +## Stage 1 - Land + +Land the prepared `stage-release-.` branch on `internal/release/.`, gated on a green official build, then run the official build on the release branch that produces the `PackageArtifacts`. Never auto-complete a pull request that needs elevation. + +Read and follow [references/stage-1-land.md](references/stage-1-land.md). + +## Stage 2 - Publish and Promote + +Publish the packages to nuget.org (the user runs `dotnet nuget push`; the agent never handles API keys), then promote the shipping build to the public `.NET ` channel. The internal build is auto-assigned only to the `.NET Internal` channel (whose symbols go to an internal isolated feed), so promoting it to the public `.NET ` channel is a **required manual step** -- without it the shipped packages have no public symbols. The agent runs the promotion only after the user confirms the BAR id and target channel (irreversible once packages flow). + +Read and follow [references/stage-2-publish-and-promote.md](references/stage-2-publish-and-promote.md). + +## Next + +Once the packages are published and the build promoted, run **validate-release** to verify the symbols on msdl and reconcile the branches. diff --git a/.github/agents/release-manager/publish-release/references/stage-1-land.md b/.github/agents/release-manager/publish-release/references/stage-1-land.md new file mode 100644 index 00000000000..0ebade8ef9f --- /dev/null +++ b/.github/agents/release-manager/publish-release/references/stage-1-land.md @@ -0,0 +1,53 @@ +# Stage 1 - Land + +After the prepare-release playbook is committed and reviewed (Prepare Internal Branch, then Update Dependencies), land the `stage-release-.` branch on `internal/release/.` -- gated on a green official build. + +This stage is operational, not file-editing: it pushes existing commits and queues pipelines rather than producing new commits. Because it pushes to the internal remote and can land changes irreversibly, it runs **only on explicit user instruction** and pauses for confirmation before every push, pipeline queue, and land action. It produces no commit of its own. + +## Prerequisites + +- The prepare-release playbook is complete and the working tree is clean on `stage-release-.`. +- Push access to the internal release repository, and access to run its official build pipeline. + +## Resolve the internal remote + +The internal remote is distinct from the public GitHub remote (`dotnet`). Resolve it by its URL rather than by name -- the remote name varies between clones (`git remote -v`). It is referred to below as ``. + +## Sub-stage 1 - Run CI on the stage branch + +Do this only when the user instructs you to push. + +1. Push the stage branch to the internal remote: + + ``` + git push stage-release-. + ``` + +2. Run the official build pipeline on the stage branch and wait for it to complete **successfully**. Do not land the branch until that run is confirmed green. If it fails, investigate, fix on the stage branch (each fix is a fresh commit), and re-run. + +## Sub-stage 2 - Land on the internal release branch + +Land only after the stage-branch pipeline is confirmed successful, and only when the user has chosen an option and confirmed. There are two options. + +### Option 1 - Direct push (bypasses PR review) + +Only valid once the stage-branch pipeline is green -- that run is the only readiness gate: + +``` +git push stage-release-.:internal/release/. +``` + +### Option 2 - Internal pull request + +- Create a pull request from `stage-release-.` targeting `internal/release/.` on the internal remote. +- Complete it with **Rebase and fast-forward** (not Squash), preserving each sub-stage commit as a linear history so it matches Option 1's direct push and keeps each sub-stage's actions auditable. Do not squash. + +Never auto-complete a pull request that requires JIT elevation or repository admin-setting changes; hand those actions to the user. + +## Sub-stage 3 - Run the official build + +After the changes land on `internal/release/.`, run the official build pipeline on that branch to produce the official release build whose artifacts will be published. + +## After the stage + +This stage produces no repository commit. Once the official build on `internal/release/.` succeeds and produces the `PackageArtifacts`, continue with **Stage 2 - Publish and Promote**. diff --git a/.github/agents/release-manager/publish-release/references/stage-2-publish-and-promote.md b/.github/agents/release-manager/publish-release/references/stage-2-publish-and-promote.md new file mode 100644 index 00000000000..76d932cb3ff --- /dev/null +++ b/.github/agents/release-manager/publish-release/references/stage-2-publish-and-promote.md @@ -0,0 +1,35 @@ +# Stage 2 - Publish and Promote + +After the Land stage (publish-release Stage 1) lands the release on `internal/release/.` and its official build produces the `PackageArtifacts`, publish the packages to nuget.org and promote the shipping build to the public channel so its symbols publish to the Microsoft symbol server (msdl). + +This stage is operational and produces no commit. Both actions are **irreversible**: publishing is human-gated (the agent prepares and reviews the package set but never runs `dotnet nuget push` itself and never handles API keys), and the channel promotion runs only after the user confirms. Verifying that the shipped symbols actually landed on msdl is the next playbook -- **validate-release**. + +## Prerequisites + +- The Land stage is complete: the official build on `internal/release/.` succeeded and produced the `PackageArtifacts`. +- For the channel promotion: the `darc` CLI, authenticated to the Build Asset Registry (BAR). + +## Sub-stage 1 - Prepare and publish packages + +Publishing is irreversible (a published version cannot be overwritten or truly deleted) and requires nuget.org API keys, so the agent only prepares the set; the user runs the push. + +1. Download and extract the `PackageArtifacts` from the official build. +2. Stage the packages to publish into a clean folder, **excluding**: + - `Microsoft.Internal.*` (always). + - Any packages the user names to hold back for this release. Confirm the exact list, and flag template/tooling packages (for example `*.ProjectTemplates`) for an explicit decision. +3. Present the excluded and to-publish lists for the user to review. +4. Two nuget.org accounts are involved: almost all packages publish from the **dotnetframework** account; **`Microsoft.Agents.AI.ProjectTemplates`** publishes from the **MicrosoftAgentFramework** account, so it must be pushed separately with that account's key. +5. The **user** runs `dotnet nuget push` with the appropriate API key(s). Never run the push, and never handle the API keys. + +## Sub-stage 2 - Promote the shipping build to the public channel + +The packages you just published to nuget.org were built by the official build on `internal/release/.`. A darc **default-channel** rule auto-assigns that build to the **`.NET Internal`** channel, whose symbols publish to an internal isolated feed -- **not** the public Microsoft symbol server (msdl). The public **`.NET `** channel (which does publish symbols to msdl) is only auto-assigned to builds from the *public* `release/.` branch, and **no subscription promotes the internal build to it**. So the shipping build's symbols reach msdl only after you **manually promote it** to the public channel. Skipping this leaves the shipped packages with no public symbols / Source Link -- the failure this sub-stage exists to prevent. + +1. Identify the shipping build's **BAR id** -- the BAR build for the official `internal/release/.` build whose artifacts you published. Confirm its commit matches the `repository/commit` embedded in a shipped `.nuspec`. `darc get-build --id ` should show it on `.NET Internal` and typically **not** yet on `.NET `. +2. Find the public channel id: `darc get-channels` -> the entry named exactly `.NET ` (no `Internal`/`Eng`/`Private`/`Workload` suffix). +3. Promote the build to the public channel with `darc add-build-to-channel --id --channel ".NET "` -- **confirm the BAR id and channel with the user before triggering**, since this publishes its symbols to msdl (`SymbolTargetType: Public`) **and** flows its packages downstream to that channel, and is **irreversible once packages flow**. A non-blocking "Build validation audit failure for production channel" warning is expected. +4. Wait for the promotion build to complete, then confirm: `darc get-build --id ` now lists `.NET ` among its channels. + +## After the stage + +This stage produces no repository commit. Next, run the **validate-release** playbook: verify Source Link and symbols on msdl (Stage 1), reconcile the branches (Stage 2), and confirm the support-page listing (Stage 3). diff --git a/.github/agents/release-manager/validate-release/README.md b/.github/agents/release-manager/validate-release/README.md new file mode 100644 index 00000000000..ab7039a8ea2 --- /dev/null +++ b/.github/agents/release-manager/validate-release/README.md @@ -0,0 +1,27 @@ +# Validate Release + +Confirms a published `dotnet/extensions` release is correct and finalizes it: verify that the shipped symbols are on the Microsoft symbol server (msdl) with working Source Link, then reconcile the internal, public, and `main` branches. + +Run this playbook after **publish-release**. Stage 1 is automated symbol verification; Stage 2 stages the reconciliation merges (pushing and PR completion are left to the user); Stage 3 confirms the support-page listing. + +## Stage 1 - Verify Source Link and Symbols + +Run the Source Link sweep against the published packages until every library package reports `valid` on msdl. This is the **release sign-off gate** -- a persistent `symbols-not-indexed` result means the shipping build never reached the public `.NET ` channel (see publish-release, Stage 2). + +Read and follow [references/stage-1-verify-source-link.md](references/stage-1-verify-source-link.md). + +## Stage 2 - Reconcile Branches + +Merge the internal release branch back out to the public `release/.` branch (discarding the internal-only infrastructure changes), then merge that public branch into `main` (reverting the stable-versioning flip). Each merge lands as a merge commit (never squashed); the agent stages the merges and shows the diff, but pushing and completing the merge-commit PRs (which need elevation) are user-directed. + +Read and follow [references/stage-2-reconcile-branches.md](references/stage-2-reconcile-branches.md). + +## Stage 3 - Confirm the Support-Page Update + +The .NET Platform Extensions support page is maintained by a partner team. Confirm that a pull request updating it has been opened for this release, review it, and flag any newly published or recently-stabilized packages that are missing from the list. + +Read and follow [references/stage-3-support-page.md](references/stage-3-support-page.md). + +## Done + +The three stages complete the branch-level release and its verification. Tagging and publishing the GitHub release notes are the **write-release-notes** playbook. diff --git a/.github/agents/release-manager/validate-release/references/stage-1-verify-source-link.md b/.github/agents/release-manager/validate-release/references/stage-1-verify-source-link.md new file mode 100644 index 00000000000..e4ae8264ead --- /dev/null +++ b/.github/agents/release-manager/validate-release/references/stage-1-verify-source-link.md @@ -0,0 +1,31 @@ +# Stage 1 - Verify Source Link and Symbols + +The publish-release playbook published the packages to nuget.org and promoted the shipping build to the public `.NET ` channel. Only after that promotion do the shipped symbols publish to the Microsoft symbol server (msdl). Verify Source Link and symbol-server availability -- this is the **release sign-off gate**. + +## Prerequisites + +- publish-release is complete: packages published to nuget.org and the shipping build promoted to the public `.NET ` channel. +- The `sourcelink` and `dotnet-symbol` global tools (`dotnet tool install -g sourcelink`; `dotnet tool install -g dotnet-symbol`). + +## Verify + +Run the Source Link sweep against the folder of published packages: + +``` +scripts/Test-SourceLink.ps1 -PackageDir +``` + +For each `.nupkg` the script extracts a lib DLL, pulls the matching PDB from the Microsoft symbol server (msdl) via `dotnet-symbol`, and runs `sourcelink test`. Each package reports one of: + +- `valid` -- Source Link resolved and the symbols were on msdl. +- `sourcelink-FAILED` -- symbols found, but Source Link did not validate (investigate). +- `symbols-not-indexed` -- the PDB is not yet on msdl (still "Validating..." on nuget.info; re-run later). +- `no-lib-dll` -- template/tooling package with no `lib/**/*.dll` (expected). + +Indexing on msdl lags the promotion, so `symbols-not-indexed` immediately afterward is expected -- **re-run until every library package is `valid`**. If packages stay `symbols-not-indexed` well after the promotion, re-confirm the shipping build is actually on the public `.NET ` channel (`darc get-build --id ` should list `.NET `); an internal-only build's symbols go to the internal isolated feed and never reach msdl. Investigate any `sourcelink-FAILED`. + +**Do not sign off the release until every library package reports `valid`.** + +## After the stage + +Once every library package is `valid`, the release symbols are public. Continue with **Stage 2 - Reconcile Branches**. diff --git a/.github/skills/prepare-release/references/stage-5-reconcile-branches.md b/.github/agents/release-manager/validate-release/references/stage-2-reconcile-branches.md similarity index 80% rename from .github/skills/prepare-release/references/stage-5-reconcile-branches.md rename to .github/agents/release-manager/validate-release/references/stage-2-reconcile-branches.md index 3870b3169a4..4d9635876ba 100644 --- a/.github/skills/prepare-release/references/stage-5-reconcile-branches.md +++ b/.github/agents/release-manager/validate-release/references/stage-2-reconcile-branches.md @@ -1,8 +1,8 @@ -# Stage 5 - Reconcile Branches +# Stage 2 - Reconcile Branches -After Stage 4 publishes and verifies the release, reconcile the branches: merge the internal release branch back out to the public release branch, then merge the public release branch into `main`. This is a post-publish activity. +After Stage 1 verifies the published symbols, reconcile the branches: merge the internal release branch back out to the public release branch, then merge the public release branch into `main`. This is the final release activity. -Like Stages 3 and 4, this stage is operational: it stages merges and creates commits on throwaway `merge/*` branches, but it does **not** push or complete pull requests on its own. The agent stages each merge, applies the required file "doctoring", commits, and shows the diff for review; **pushing and completing the PRs (which need JIT elevation / admin settings) are user-directed**, and each merge PR must land as a merge commit (never squashed). +Like the publish-release stages, this stage is operational: it stages merges and creates commits on throwaway `merge/*` branches, but it does **not** push or complete pull requests on its own. The agent stages each merge, applies the required file "doctoring", commits, and shows the diff for review; **pushing and completing the PRs (which need JIT elevation / admin settings) are user-directed**, and each merge PR must land as a merge commit (never squashed). Run each sub-stage only when the user instructs it. Sub-stage 2 depends on Sub-stage 1's PR having already merged into the public release branch. @@ -46,7 +46,7 @@ Carry the version bumps and internal product changes out to the public `release/ Compare this merge's staged file set (`git diff --cached --stat`) against them and flag anomalies to the user before committing: - `eng/Version.Details.xml` and `eng/Versions.props` appear in every release and must be here too (including the stabilization flip). - The internal-only infrastructure files (`Directory.Build.props`, `NuGet.config`, `azure-pipelines.yml`, `eng/pipelines/templates/BuildAndTest.yml`) are absent in every recent release and must be absent here too. - - `.github/skills/**` and other tooling files are excluded by recent releases -- flag if present. + - Tooling files such as `.github/agents/**` and `.github/skills/**` are excluded by recent releases -- flag if present. - The remaining files should be version bumps plus intentional product/test backports. Flag anything unexpected: an unusually large or empty change set, missing versioning, leaked infrastructure, or files no prior release touched. 6. Review the staged diff: it should be version bumps + product changes, with **no** infrastructure changes. Then commit: @@ -89,4 +89,4 @@ Do this only after Sub-stage 1's PR has merged into `release/.`. I ## After the stage -Stage 5 produces merge commits on `merge/*` branches but does not push or complete the pull requests. The remaining release activities (dotnet-public mirror, release notes and tag, and the support-page update) are outside the scope of these stages. +This stage produces merge commits on `merge/*` branches but does not push or complete the pull requests. Tagging and publishing the release notes are handled by the **write-release-notes** playbook. Next, confirm the support-page listing (Stage 3). diff --git a/.github/agents/release-manager/validate-release/references/stage-3-support-page.md b/.github/agents/release-manager/validate-release/references/stage-3-support-page.md new file mode 100644 index 00000000000..72bedd2a617 --- /dev/null +++ b/.github/agents/release-manager/validate-release/references/stage-3-support-page.md @@ -0,0 +1,20 @@ +# Stage 3 - Confirm the Support-Page Update + +The [.NET Platform Extensions support policy page](https://dotnet.microsoft.com/en-us/platform/support/policy/extensions) lists the supported packages and their current versions. It is maintained by a **partner team**, so this stage is a **review**, not an edit: confirm their update lands and is complete. + +## Steps + +1. **Find the pull request** into [`dotnet/website`](https://github.com/dotnet/website) that updates the extensions support page for this release (it edits `website/src/netlandingpage/Pages/platform/support/policy/extensions.cshtml`). Search open and recently-merged PRs. +2. **Review it** for this release: + - The supported version and its release date are updated to this release. + - The previous minor version has moved to the out-of-support table with the correct end-of-support date. +3. **Check the package list** against what shipped. Compare the page's listed packages to the packages published in this release, and flag any that are missing -- in particular: + - **Newly published** packages (new in this release). + - **Recently-stabilized** packages (previously preview, now stable as of this release). + + Exclude preview-only packages, which are not listed. +4. If the pull request is missing or incomplete, raise it with the partner team. + +## After the stage + +This is the final validation step. The release is complete once its symbols are public (Stage 1), the branches are reconciled (Stage 2), and the support-page listing is confirmed. diff --git a/.github/skills/prepare-release/scripts/Test-SourceLink.ps1 b/.github/agents/release-manager/validate-release/scripts/Test-SourceLink.ps1 similarity index 100% rename from .github/skills/prepare-release/scripts/Test-SourceLink.ps1 rename to .github/agents/release-manager/validate-release/scripts/Test-SourceLink.ps1 diff --git a/.github/skills/write-release-notes/SKILL.md b/.github/agents/release-manager/write-release-notes/README.md similarity index 90% rename from .github/skills/write-release-notes/SKILL.md rename to .github/agents/release-manager/write-release-notes/README.md index 0455eef8e0d..963b2f22982 100644 --- a/.github/skills/write-release-notes/SKILL.md +++ b/.github/agents/release-manager/write-release-notes/README.md @@ -1,15 +1,8 @@ ---- -name: write-release-notes -description: 'Draft release notes for a dotnet/extensions release. Gathers merged PRs, assigns them to packages by file path, categorizes by area and impact, tracks experimental API changes, and produces formatted markdown suitable for a GitHub release. Handles both monthly full releases and targeted intra-month patch releases.' -agent: 'agent' -tools: ['github/*', 'sql', 'ask_user'] ---- - # Release Notes -Draft release notes for a `dotnet/extensions` release. This skill gathers merged PRs between two tags, maps them to affected packages by examining changed file paths, categorizes entries by area and impact, audits experimental API changes, and produces concise markdown suitable for a GitHub release. +Draft release notes for a `dotnet/extensions` release. This playbook gathers merged PRs between two tags, maps them to affected packages by examining changed file paths, categorizes entries by area and impact, audits experimental API changes, and produces concise markdown suitable for a GitHub release. -> **User confirmation required: This skill NEVER publishes a GitHub release without explicit user confirmation.** The user must review and approve the draft before any release is created. +> **User confirmation required: This playbook NEVER publishes a GitHub release without explicit user confirmation.** The user must review and approve the draft before any release is created. ## Context diff --git a/.github/skills/write-release-notes/references/categorize-entries.md b/.github/agents/release-manager/write-release-notes/references/categorize-entries.md similarity index 98% rename from .github/skills/write-release-notes/references/categorize-entries.md rename to .github/agents/release-manager/write-release-notes/references/categorize-entries.md index 6e570167c07..e302bd6a5ff 100644 --- a/.github/skills/write-release-notes/references/categorize-entries.md +++ b/.github/agents/release-manager/write-release-notes/references/categorize-entries.md @@ -11,7 +11,7 @@ For each candidate PR, assign one of these categories based on the primary inten | What's Changed | `changed` | Features, bug fixes, API improvements, performance, breaking changes | | Documentation Updates | `docs` | PRs whose sole purpose is documentation | | Test Improvements | `tests` | Adding, fixing, or improving tests | -| Repository Infrastructure Updates | `infra` | CI/CD, dependency bumps, version bumps, build system, skills | +| Repository Infrastructure Updates | `infra` | CI/CD, dependency bumps, version bumps, build system, agents and skills | **Decision rules:** - If a PR modifies files under `src/Libraries/` or `src/Generators/` or `src/Analyzers/`, it is `changed` (even if it also touches docs or tests) diff --git a/.github/skills/write-release-notes/references/collect-prs.md b/.github/agents/release-manager/write-release-notes/references/collect-prs.md similarity index 100% rename from .github/skills/write-release-notes/references/collect-prs.md rename to .github/agents/release-manager/write-release-notes/references/collect-prs.md diff --git a/.github/skills/write-release-notes/references/editorial-rules.md b/.github/agents/release-manager/write-release-notes/references/editorial-rules.md similarity index 99% rename from .github/skills/write-release-notes/references/editorial-rules.md rename to .github/agents/release-manager/write-release-notes/references/editorial-rules.md index 9efe10704b7..5c2aa24b24c 100644 --- a/.github/skills/write-release-notes/references/editorial-rules.md +++ b/.github/agents/release-manager/write-release-notes/references/editorial-rules.md @@ -91,7 +91,7 @@ PRs that maintain the development environment: - CI/CD workflow changes - Dependency updates (Dependabot) - Build system changes -- Copilot instructions and skill updates +- Copilot instructions, agent, and skill updates PRs that touch test code should never be categorized as Infrastructure. diff --git a/.github/skills/write-release-notes/references/experimental-features.md b/.github/agents/release-manager/write-release-notes/references/experimental-features.md similarity index 100% rename from .github/skills/write-release-notes/references/experimental-features.md rename to .github/agents/release-manager/write-release-notes/references/experimental-features.md diff --git a/.github/skills/write-release-notes/references/format-template.md b/.github/agents/release-manager/write-release-notes/references/format-template.md similarity index 100% rename from .github/skills/write-release-notes/references/format-template.md rename to .github/agents/release-manager/write-release-notes/references/format-template.md diff --git a/.github/skills/write-release-notes/references/package-areas.md b/.github/agents/release-manager/write-release-notes/references/package-areas.md similarity index 100% rename from .github/skills/write-release-notes/references/package-areas.md rename to .github/agents/release-manager/write-release-notes/references/package-areas.md diff --git a/.github/skills/write-release-notes/references/sql-storage.md b/.github/agents/release-manager/write-release-notes/references/sql-storage.md similarity index 100% rename from .github/skills/write-release-notes/references/sql-storage.md rename to .github/agents/release-manager/write-release-notes/references/sql-storage.md diff --git a/.github/skills/prepare-release/SKILL.md b/.github/skills/prepare-release/SKILL.md deleted file mode 100644 index 03d3e6d5a59..00000000000 --- a/.github/skills/prepare-release/SKILL.md +++ /dev/null @@ -1,53 +0,0 @@ ---- -name: prepare-release -description: Prepares the repository for an internal release branch. Use this when asked to "prepare for a release", "prepare internal release branch", or similar release preparation tasks. ---- - -# Prepare Release - -Prepares a `dotnet/extensions` public release branch (`release/.`) for the internal release process on the corresponding `internal/release/.` branch. - -The preparation is organized into ordered stages. Work through them in sequence, loading each stage's reference file only when you reach that stage. - -## Stage workflow - -Complete stages strictly in order. Treat each stage -- and each sub-stage of a stage that has them -- as an independent, committable unit. For every stage or sub-stage: - -1. Apply the changes described in the stage's reference file. -2. Prompt the user to review the changes (summarize what changed and show the diff), unless the stage's reference file directs you to commit automatically. -3. Wait for the user's approval before committing, unless the stage's reference file directs automatic commits. -4. Create a single commit that contains only that stage's (or sub-stage's) changes. - -Every stage gets its own commit, and every sub-stage gets its own commit. Never combine multiple stages or sub-stages into a single commit. Never push until the user explicitly instructs it, whatever a stage's commit cadence. Stages 3, 4, and 5 are the exception: they are operational -- they push commits, queue pipelines, publish/verify packages, and stage merge commits rather than producing an ordinary stage commit. - -Commits this skill creates land in public dotnet/extensions history -- the stage commits flow out through the non-squash internal-to-public merge, and Stage 5's reconciliation merge commits land directly. Keep the `Co-authored-by: Copilot` trailer on those commits but omit the `Copilot-Session` trailer. - -## Stage 1 - Prepare Internal Branch - -Apply the internal-release infrastructure changes to the branch: suppress `NU1507`, remove the NuGet package source mapping, switch on stable/release versioning, add private-feed credential setup to the build template, comment out integration tests, and remove the code-coverage pipeline stage. Never change version numbers here -- those flow via Dependency Flow automation. - -Read and follow [references/stage-1-prepare-internal-branch.md](references/stage-1-prepare-internal-branch.md). - -## Stage 2 - Update Dependencies - -Update the branch's product dependencies to the pending .NET 9, .NET 8, and .NET 10 servicing releases using `darc update-dependencies`. The BAR build IDs come from the release.dot.net Release Tracker, which is behind Microsoft auth and unreachable by the agent, so the user supplies them (pasted `ReleaseManifest.json` or a downloaded copy). This stage has three sub-stages, each its own commit: .NET 9, then .NET 8, then .NET 10. - -Read and follow [references/stage-2-update-dependencies.md](references/stage-2-update-dependencies.md). - -## Stage 3 - Validate and Land - -Validate the prepared `stage-release-.` branch with an official build, then land it on the `internal/release/.` branch. This stage is operational -- it pushes existing commits and queues the `official-build-pipeline` pipeline rather than creating new commits -- so it runs only on explicit user instruction and pauses before every push and land action. It gates landing on a green stage-branch pipeline and never auto-completes a pull request that needs elevation. - -Read and follow [references/stage-3-validate-and-land.md](references/stage-3-validate-and-land.md). - -## Stage 4 - Publish and Verify - -After Stage 3 lands the release and its official build produces the package artifacts, publish the packages to nuget.org and verify Source Link on the shipped symbols. Like Stage 3 this stage is operational and produces no commit; publishing is human-gated and irreversible, so the agent stages and reviews the package set but never runs the push or handles API keys, while the Source Link verification (via `scripts/Test-SourceLink.ps1`) is automated. - -Read and follow [references/stage-4-publish-and-verify.md](references/stage-4-publish-and-verify.md). - -## Stage 5 - Reconcile Branches - -After Stage 4 publishes the release, merge the internal release branch back out to the public `release/.` branch (discarding the internal-only infrastructure changes) and then merge that public branch into `main` (reverting the stable-versioning flip). Like Stages 3 and 4 it is operational: the agent stages each merge, applies the required file doctoring, commits on a `merge/*` branch, and shows the diff -- but pushing and completing the merge-commit PRs (which need elevation) are user-directed. - -Read and follow [references/stage-5-reconcile-branches.md](references/stage-5-reconcile-branches.md). diff --git a/.github/skills/prepare-release/references/stage-3-validate-and-land.md b/.github/skills/prepare-release/references/stage-3-validate-and-land.md deleted file mode 100644 index 3547dd24376..00000000000 --- a/.github/skills/prepare-release/references/stage-3-validate-and-land.md +++ /dev/null @@ -1,76 +0,0 @@ -# Stage 3 - Validate and Land - -After Stages 1 and 2 are committed and reviewed, validate the `stage-release-.` branch with an official build and land it on the `internal/release/.` branch. - -This stage is operational, not file-editing: it pushes existing commits and queues pipelines rather than producing new commits. Because it pushes to the internal remote and can land changes irreversibly, it runs **only on explicit user instruction** and pauses for confirmation before every push, pipeline queue, and land action. This overrides the commit-per-stage cadence in `SKILL.md` -- Stage 3 produces no commit of its own. - -## Prerequisites - -- Stages 1 and 2 are complete and the working tree is clean on `stage-release-.`. - # run the official build pipeline on the branch and confirm it succeeds -- You have push access to the internal release repository. - -## Resolve the internal remote - -The internal remote is the one whose URL is ``. Resolve it by URL rather than by name -- the remote name varies between clones: - -``` -git remote -v -``` - -Use the remote name that maps to that URL (referred to below as ``). - -## Sub-stage 1 - Validate the stage branch - -Do this only when the user instructs you to push. - -1. Push the stage branch to the internal remote: - - ``` - git push stage-release-. - ``` - -2. Queue the official build pipeline against the stage branch: - - ``` - # run the official build pipeline on the branch and confirm it succeeds - ``` - -3. Poll the run until it completes and confirm the result is `succeeded`: - - ``` - # run the official build pipeline on the branch and confirm it succeeds - ``` - - Do not land the branch until this run is confirmed green. If it fails, investigate, fix on the stage branch (each fix is a fresh commit), and re-run. - -## Sub-stage 2 - Land on the internal release branch - -Land only after the stage-branch pipeline is confirmed successful, and only when the user has chosen an option and confirmed. There are two options. - -### Option 1 - Direct push (bypasses PR review) - -Only valid once the stage-branch pipeline is green -- that run is the only readiness gate: - -``` -git push stage-release-.:internal/release/. -``` - -### Option 2 - Internal pull request - -- Create a pull request from `stage-release-.` targeting `internal/release/.` at ``. -- Complete it with **Rebase and fast-forward** (not Squash), preserving each sub-stage commit as a linear history so it matches Option 1's direct push and keeps each sub-stage's actions auditable. Do not squash. - -Never auto-complete a pull request that requires JIT elevation or repository admin-setting changes; hand those actions to the user. - -## Sub-stage 3 - Run the official build - -After the changes land on `internal/release/.`, queue `official-build-pipeline` against that branch to produce the official release build whose artifacts will be published: - -``` - # run the official build pipeline on the branch and confirm it succeeds -``` - -## After the stage - -Stage 3 produces no repository commit. Once the official build on `internal/release/.` succeeds, the branch is ready for the release-publication steps (publishing packages, channel promotion, release notes), which are outside this skill. diff --git a/.github/skills/prepare-release/references/stage-4-publish-and-verify.md b/.github/skills/prepare-release/references/stage-4-publish-and-verify.md deleted file mode 100644 index 7ed1fe61b92..00000000000 --- a/.github/skills/prepare-release/references/stage-4-publish-and-verify.md +++ /dev/null @@ -1,36 +0,0 @@ -# Stage 4 - Publish and Verify - -After Stage 3 lands the release on `internal/release/.` and its official build produces the `PackageArtifacts`, publish the packages to nuget.org and verify Source Link on the shipped symbols. - -Like Stage 3, this stage is operational and produces no commit. Publishing is **human-gated and irreversible**, so the agent prepares and reviews the package set but never runs `dotnet nuget push` itself and never handles API keys. The Source Link verification is fully automated. - -## Prerequisites - -- Stage 3 complete: the official build on `internal/release/.` succeeded and produced the `PackageArtifacts`. -- For verification: the `sourcelink` and `dotnet-symbol` global tools (`dotnet tool install -g sourcelink`; `dotnet tool install -g dotnet-symbol`). - -## Sub-stage 1 - Prepare and publish packages - -Publishing is irreversible (a published version cannot be overwritten or truly deleted) and requires nuget.org API keys, so the agent only prepares the set; the user runs the push. - -1. Download and extract the `PackageArtifacts` from the official build. -2. Stage the packages to publish into a clean folder, **excluding**: - - `Microsoft.Internal.*` (always). - - Any packages the user names to hold back for this release. Confirm the exact list, and flag template/tooling packages (for example `*.ProjectTemplates`) for an explicit decision. -3. Present the excluded and to-publish lists for the user to review. -4. Two nuget.org accounts are involved: almost all packages publish from the **dotnetframework** account; **`Microsoft.Agents.AI.ProjectTemplates`** publishes from the **MicrosoftAgentFramework** account, so it must be pushed separately with that account's key. -5. The **user** runs `dotnet nuget push` with the appropriate API key(s). Never run the push, and never handle the API keys. - -## Sub-stage 2 - Verify Source Link and symbols - -Once the packages and their symbols are published (symbol publishing happens via the channel-promotion step and lags the nuget push), verify Source Link and symbol-server availability: - -``` -scripts/Test-SourceLink.ps1 -PackageDir -``` - -Each package reports `valid`, `sourcelink-FAILED`, `symbols-not-indexed`, or `no-lib-dll` (template/tooling package). Symbol indexing on nuget.org lags publishing, so `symbols-not-indexed` right after publish is expected -- **re-run until every library package is `valid`**. Investigate any `sourcelink-FAILED`. - -## After the stage - -Stage 4 produces no repository commit. The internal-to-public and release-to-main merges are handled by Stage 5. The remaining activities (dotnet-public mirror, release notes and tag, and the support-page update) are outside the scope of these stages. From 92ece5d5e0c8dd38fcdca6cd6b9a96bdbef20c9c Mon Sep 17 00:00:00 2001 From: Jeff Handley Date: Mon, 20 Jul 2026 14:13:49 -0700 Subject: [PATCH 12/15] Fix release manager review feedback Normalize the end-to-end stage numbering, correct workflow and script references, make Source Link validation portable, add timeless starter prompts for release-manager sessions, and classify workflow changes as release-note infrastructure. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: d88cce81-2b82-459a-a747-f456ea15a180 --- .github/agents/release-manager.agent.md | 33 +++++++++++++++++-- .../release-manager/prepare-release/README.md | 2 +- .../references/stage-2-update-dependencies.md | 2 +- .../release-manager/publish-release/README.md | 8 ++--- .../{stage-1-land.md => stage-3-land.md} | 4 +-- ...mote.md => stage-4-publish-and-promote.md} | 6 ++-- .../validate-release/README.md | 18 +++++----- ...-link.md => stage-5-verify-source-link.md} | 6 ++-- ...nches.md => stage-6-reconcile-branches.md} | 6 ++-- ...upport-page.md => stage-7-support-page.md} | 4 +-- .../scripts/Test-SourceLink.ps1 | 9 +++-- .../references/categorize-entries.md | 2 +- 12 files changed, 66 insertions(+), 34 deletions(-) rename .github/agents/release-manager/publish-release/references/{stage-1-land.md => stage-3-land.md} (97%) rename .github/agents/release-manager/publish-release/references/{stage-2-publish-and-promote.md => stage-4-publish-and-promote.md} (95%) rename .github/agents/release-manager/validate-release/references/{stage-1-verify-source-link.md => stage-5-verify-source-link.md} (89%) rename .github/agents/release-manager/validate-release/references/{stage-2-reconcile-branches.md => stage-6-reconcile-branches.md} (97%) rename .github/agents/release-manager/validate-release/references/{stage-3-support-page.md => stage-7-support-page.md} (89%) diff --git a/.github/agents/release-manager.agent.md b/.github/agents/release-manager.agent.md index 1e8d3abc005..665365b3702 100644 --- a/.github/agents/release-manager.agent.md +++ b/.github/agents/release-manager.agent.md @@ -11,6 +11,11 @@ description: > "promote the build to the public channel", "fix missing/public symbols for a release", "reconcile release branches", "write/draft release notes for ", and other dotnet/extensions release operations. + RECOMMENDED STARTER PROMPTS: "Where are we in the release process?", "Explain the + dotnet/extensions release process to me.", "Help me prepare a dotnet/extensions release branch.", + "Help me update dependencies on an already prepared release branch.", "Help me land and publish a + prepared dotnet/extensions release.", "Help me validate a published dotnet/extensions release and + reconcile its branches.", or "Help me draft release notes for a dotnet/extensions release tag." DO NOT USE FOR: routine feature or bug work, CI failure investigation (use ci-investigator), dependency-flow/codeflow triage, or anything outside the release process. --- @@ -22,6 +27,30 @@ process from branch preparation through publishing, symbol availability, branch release notes. Pick the right playbook, follow it stage by stage, and keep the human in the loop at every gate. +## Starting a session + +When the user asks where the release process stands, assess the current release state without relying +on this session's history: inspect the available branch, commit, pull-request, and build information; +identify what is complete and what remains; and state any missing context. Earlier stages may have +happened in another session. Do not make changes while assessing status. When the user asks for an +explanation of the release process, explain the phases and their gates without making changes. + +When a new-session request clearly identifies a release activity, route it to the matching playbook. +When the user appears unsure how to begin -- for example, they ask for general release guidance, use a +vague request such as "help with a release," or do not identify a release activity -- do not assume a +playbook or make changes. Briefly explain that the release process has distinct phases, then present +these recommended starter prompts for the user to choose or adapt: + +- "Where are we in the release process?" +- "Explain the dotnet/extensions release process to me." +- "Help me prepare a dotnet/extensions release branch." +- "Help me update dependencies on an already prepared release branch." +- "Help me land and publish a prepared dotnet/extensions release." +- "Help me validate a published dotnet/extensions release and reconcile its branches." +- "Help me draft release notes for a dotnet/extensions release tag." + +Wait for the user to select or clarify a starting point before loading a playbook or taking action. + ## Playbooks Select the playbook that matches the request, read its README first, then load each stage or @@ -62,7 +91,7 @@ verification script lives at `release-manager/validate-release/scripts/Test-Sour The release runs as three playbooks in order, then release notes: 1. **prepare-release** -- Stage 1 prepare the internal branch (no version-number edits); Stage 2 update .NET 9, then .NET 8, then .NET 10 dependencies (three sub-stages). Commit-producing preparation. -2. **publish-release** -- Stage 1 land on `internal/release/.` (gated on a green official build); Stage 2 publish to nuget.org and **promote the shipping build to the public `.NET ` channel** (required -- this is what puts symbols on msdl). Operational and irreversible. -3. **validate-release** -- Stage 1 verify Source Link/symbols on msdl until every library package is `valid` (the release sign-off gate); Stage 2 reconcile internal -> public `release/.` -> `main`; Stage 3 confirm the partner team's support-page update. +2. **publish-release** -- Stage 3 land on `internal/release/.` (gated on a green official build); Stage 4 publish to nuget.org and **promote the shipping build to the public `.NET ` channel** (required -- this is what puts symbols on msdl). Operational and irreversible. +3. **validate-release** -- Stage 5 verify Source Link/symbols on msdl until every library package is `valid` (the release sign-off gate); Stage 6 reconcile internal -> public `release/.` -> `main`; Stage 7 confirm the partner team's support-page update. Tagging and publishing the GitHub release notes are handled by the **write-release-notes** playbook. diff --git a/.github/agents/release-manager/prepare-release/README.md b/.github/agents/release-manager/prepare-release/README.md index 7f3cf49084c..bf2c8bc350f 100644 --- a/.github/agents/release-manager/prepare-release/README.md +++ b/.github/agents/release-manager/prepare-release/README.md @@ -15,7 +15,7 @@ Complete stages strictly in order. Treat each stage -- and each sub-stage of a s Every stage gets its own commit, and every sub-stage gets its own commit. Never combine multiple stages or sub-stages into a single commit. Never push until the user explicitly instructs it. -Commits this playbook creates land in public dotnet/extensions history -- they flow out later through the non-squash internal-to-public merge (validate-release, Stage 2). Keep the `Co-authored-by: Copilot` trailer on those commits but omit the `Copilot-Session` trailer. +Commits this playbook creates land in public dotnet/extensions history -- they flow out later through the non-squash internal-to-public merge (validate-release, Stage 6). Keep the `Co-authored-by: Copilot` trailer on those commits but omit the `Copilot-Session` trailer. ## Stage 1 - Prepare Internal Branch diff --git a/.github/agents/release-manager/prepare-release/references/stage-2-update-dependencies.md b/.github/agents/release-manager/prepare-release/references/stage-2-update-dependencies.md index 6fc75be1379..c50da91666b 100644 --- a/.github/agents/release-manager/prepare-release/references/stage-2-update-dependencies.md +++ b/.github/agents/release-manager/prepare-release/references/stage-2-update-dependencies.md @@ -8,7 +8,7 @@ This stage has three sub-stages. Run them in order, and give each its own commit 2. Update Dependencies: .NET 8 3. Update Dependencies: .NET 10 -Commit each sub-stage automatically as you complete it -- do not pause for per-sub-stage review or approval. This overrides steps 2 and 3 of the Stage workflow in `SKILL.md`. Pushing remains a separate, user-directed step: do not push until the user explicitly instructs it (see "After the stage"). +Commit each sub-stage automatically as you complete it -- do not pause for per-sub-stage review or approval. This overrides steps 2 and 3 of the [Stage workflow](../README.md#stage-workflow). Pushing remains a separate, user-directed step: do not push until the user explicitly instructs it (see "After the stage"). Before committing each sub-stage, review the diff yourself and revert any incidental, non-dependency changes darc introduces (for example, darc sometimes adds a trailing newline to `.config/dotnet-tools.json`). Keep only the intended dependency edits for that sub-stage. diff --git a/.github/agents/release-manager/publish-release/README.md b/.github/agents/release-manager/publish-release/README.md index 346dac50676..b69c2845cae 100644 --- a/.github/agents/release-manager/publish-release/README.md +++ b/.github/agents/release-manager/publish-release/README.md @@ -4,17 +4,17 @@ Ships a prepared `dotnet/extensions` release: land the prepared branch on `inter Run this playbook after **prepare-release** is complete. Both stages are **operational** -- they push, queue pipelines, publish, and promote rather than producing ordinary commits -- and every push, publish, and promotion is **irreversible**. Run each stage only on explicit user instruction, and pause for confirmation before every irreversible action. -## Stage 1 - Land +## Stage 3 - Land Land the prepared `stage-release-.` branch on `internal/release/.`, gated on a green official build, then run the official build on the release branch that produces the `PackageArtifacts`. Never auto-complete a pull request that needs elevation. -Read and follow [references/stage-1-land.md](references/stage-1-land.md). +Read and follow [references/stage-3-land.md](references/stage-3-land.md). -## Stage 2 - Publish and Promote +## Stage 4 - Publish and Promote Publish the packages to nuget.org (the user runs `dotnet nuget push`; the agent never handles API keys), then promote the shipping build to the public `.NET ` channel. The internal build is auto-assigned only to the `.NET Internal` channel (whose symbols go to an internal isolated feed), so promoting it to the public `.NET ` channel is a **required manual step** -- without it the shipped packages have no public symbols. The agent runs the promotion only after the user confirms the BAR id and target channel (irreversible once packages flow). -Read and follow [references/stage-2-publish-and-promote.md](references/stage-2-publish-and-promote.md). +Read and follow [references/stage-4-publish-and-promote.md](references/stage-4-publish-and-promote.md). ## Next diff --git a/.github/agents/release-manager/publish-release/references/stage-1-land.md b/.github/agents/release-manager/publish-release/references/stage-3-land.md similarity index 97% rename from .github/agents/release-manager/publish-release/references/stage-1-land.md rename to .github/agents/release-manager/publish-release/references/stage-3-land.md index 0ebade8ef9f..44471ce191c 100644 --- a/.github/agents/release-manager/publish-release/references/stage-1-land.md +++ b/.github/agents/release-manager/publish-release/references/stage-3-land.md @@ -1,4 +1,4 @@ -# Stage 1 - Land +# Stage 3 - Land After the prepare-release playbook is committed and reviewed (Prepare Internal Branch, then Update Dependencies), land the `stage-release-.` branch on `internal/release/.` -- gated on a green official build. @@ -50,4 +50,4 @@ After the changes land on `internal/release/.`, run the official b ## After the stage -This stage produces no repository commit. Once the official build on `internal/release/.` succeeds and produces the `PackageArtifacts`, continue with **Stage 2 - Publish and Promote**. +This stage produces no repository commit. Once the official build on `internal/release/.` succeeds and produces the `PackageArtifacts`, continue with **Stage 4 - Publish and Promote**. diff --git a/.github/agents/release-manager/publish-release/references/stage-2-publish-and-promote.md b/.github/agents/release-manager/publish-release/references/stage-4-publish-and-promote.md similarity index 95% rename from .github/agents/release-manager/publish-release/references/stage-2-publish-and-promote.md rename to .github/agents/release-manager/publish-release/references/stage-4-publish-and-promote.md index 76d932cb3ff..5fced480d52 100644 --- a/.github/agents/release-manager/publish-release/references/stage-2-publish-and-promote.md +++ b/.github/agents/release-manager/publish-release/references/stage-4-publish-and-promote.md @@ -1,6 +1,6 @@ -# Stage 2 - Publish and Promote +# Stage 4 - Publish and Promote -After the Land stage (publish-release Stage 1) lands the release on `internal/release/.` and its official build produces the `PackageArtifacts`, publish the packages to nuget.org and promote the shipping build to the public channel so its symbols publish to the Microsoft symbol server (msdl). +After the Land stage (publish-release Stage 3) lands the release on `internal/release/.` and its official build produces the `PackageArtifacts`, publish the packages to nuget.org and promote the shipping build to the public channel so its symbols publish to the Microsoft symbol server (msdl). This stage is operational and produces no commit. Both actions are **irreversible**: publishing is human-gated (the agent prepares and reviews the package set but never runs `dotnet nuget push` itself and never handles API keys), and the channel promotion runs only after the user confirms. Verifying that the shipped symbols actually landed on msdl is the next playbook -- **validate-release**. @@ -32,4 +32,4 @@ The packages you just published to nuget.org were built by the official build on ## After the stage -This stage produces no repository commit. Next, run the **validate-release** playbook: verify Source Link and symbols on msdl (Stage 1), reconcile the branches (Stage 2), and confirm the support-page listing (Stage 3). +This stage produces no repository commit. Next, run the **validate-release** playbook: verify Source Link and symbols on msdl (Stage 5), reconcile the branches (Stage 6), and confirm the support-page listing (Stage 7). diff --git a/.github/agents/release-manager/validate-release/README.md b/.github/agents/release-manager/validate-release/README.md index ab7039a8ea2..d7bcb273e55 100644 --- a/.github/agents/release-manager/validate-release/README.md +++ b/.github/agents/release-manager/validate-release/README.md @@ -2,26 +2,26 @@ Confirms a published `dotnet/extensions` release is correct and finalizes it: verify that the shipped symbols are on the Microsoft symbol server (msdl) with working Source Link, then reconcile the internal, public, and `main` branches. -Run this playbook after **publish-release**. Stage 1 is automated symbol verification; Stage 2 stages the reconciliation merges (pushing and PR completion are left to the user); Stage 3 confirms the support-page listing. +Run this playbook after **publish-release**. Stage 5 is automated symbol verification; Stage 6 stages the reconciliation merges (pushing and PR completion are left to the user); Stage 7 confirms the support-page listing. -## Stage 1 - Verify Source Link and Symbols +## Stage 5 - Verify Source Link and Symbols -Run the Source Link sweep against the published packages until every library package reports `valid` on msdl. This is the **release sign-off gate** -- a persistent `symbols-not-indexed` result means the shipping build never reached the public `.NET ` channel (see publish-release, Stage 2). +Run the Source Link sweep against the published packages until every library package reports `valid` on msdl. This is the **release sign-off gate** -- a persistent `symbols-not-indexed` result means the shipping build never reached the public `.NET ` channel (see publish-release, Stage 4). -Read and follow [references/stage-1-verify-source-link.md](references/stage-1-verify-source-link.md). +Read and follow [references/stage-5-verify-source-link.md](references/stage-5-verify-source-link.md). -## Stage 2 - Reconcile Branches +## Stage 6 - Reconcile Branches Merge the internal release branch back out to the public `release/.` branch (discarding the internal-only infrastructure changes), then merge that public branch into `main` (reverting the stable-versioning flip). Each merge lands as a merge commit (never squashed); the agent stages the merges and shows the diff, but pushing and completing the merge-commit PRs (which need elevation) are user-directed. -Read and follow [references/stage-2-reconcile-branches.md](references/stage-2-reconcile-branches.md). +Read and follow [references/stage-6-reconcile-branches.md](references/stage-6-reconcile-branches.md). -## Stage 3 - Confirm the Support-Page Update +## Stage 7 - Confirm the Support-Page Update The .NET Platform Extensions support page is maintained by a partner team. Confirm that a pull request updating it has been opened for this release, review it, and flag any newly published or recently-stabilized packages that are missing from the list. -Read and follow [references/stage-3-support-page.md](references/stage-3-support-page.md). +Read and follow [references/stage-7-support-page.md](references/stage-7-support-page.md). ## Done -The three stages complete the branch-level release and its verification. Tagging and publishing the GitHub release notes are the **write-release-notes** playbook. +These three final stages complete the branch-level release and its verification. Tagging and publishing the GitHub release notes are the **write-release-notes** playbook. diff --git a/.github/agents/release-manager/validate-release/references/stage-1-verify-source-link.md b/.github/agents/release-manager/validate-release/references/stage-5-verify-source-link.md similarity index 89% rename from .github/agents/release-manager/validate-release/references/stage-1-verify-source-link.md rename to .github/agents/release-manager/validate-release/references/stage-5-verify-source-link.md index e4ae8264ead..35a3aa0a3f0 100644 --- a/.github/agents/release-manager/validate-release/references/stage-1-verify-source-link.md +++ b/.github/agents/release-manager/validate-release/references/stage-5-verify-source-link.md @@ -1,4 +1,4 @@ -# Stage 1 - Verify Source Link and Symbols +# Stage 5 - Verify Source Link and Symbols The publish-release playbook published the packages to nuget.org and promoted the shipping build to the public `.NET ` channel. Only after that promotion do the shipped symbols publish to the Microsoft symbol server (msdl). Verify Source Link and symbol-server availability -- this is the **release sign-off gate**. @@ -12,7 +12,7 @@ The publish-release playbook published the packages to nuget.org and promoted th Run the Source Link sweep against the folder of published packages: ``` -scripts/Test-SourceLink.ps1 -PackageDir +./.github/agents/release-manager/validate-release/scripts/Test-SourceLink.ps1 -PackageDir ``` For each `.nupkg` the script extracts a lib DLL, pulls the matching PDB from the Microsoft symbol server (msdl) via `dotnet-symbol`, and runs `sourcelink test`. Each package reports one of: @@ -28,4 +28,4 @@ Indexing on msdl lags the promotion, so `symbols-not-indexed` immediately afterw ## After the stage -Once every library package is `valid`, the release symbols are public. Continue with **Stage 2 - Reconcile Branches**. +Once every library package is `valid`, the release symbols are public. Continue with **Stage 6 - Reconcile Branches**. diff --git a/.github/agents/release-manager/validate-release/references/stage-2-reconcile-branches.md b/.github/agents/release-manager/validate-release/references/stage-6-reconcile-branches.md similarity index 97% rename from .github/agents/release-manager/validate-release/references/stage-2-reconcile-branches.md rename to .github/agents/release-manager/validate-release/references/stage-6-reconcile-branches.md index 4d9635876ba..d5553e58070 100644 --- a/.github/agents/release-manager/validate-release/references/stage-2-reconcile-branches.md +++ b/.github/agents/release-manager/validate-release/references/stage-6-reconcile-branches.md @@ -1,6 +1,6 @@ -# Stage 2 - Reconcile Branches +# Stage 6 - Reconcile Branches -After Stage 1 verifies the published symbols, reconcile the branches: merge the internal release branch back out to the public release branch, then merge the public release branch into `main`. This is the final release activity. +After Stage 5 verifies the published symbols, reconcile the branches: merge the internal release branch back out to the public release branch, then merge the public release branch into `main`. This is the final release activity. Like the publish-release stages, this stage is operational: it stages merges and creates commits on throwaway `merge/*` branches, but it does **not** push or complete pull requests on its own. The agent stages each merge, applies the required file "doctoring", commits, and shows the diff for review; **pushing and completing the PRs (which need JIT elevation / admin settings) are user-directed**, and each merge PR must land as a merge commit (never squashed). @@ -89,4 +89,4 @@ Do this only after Sub-stage 1's PR has merged into `release/.`. I ## After the stage -This stage produces merge commits on `merge/*` branches but does not push or complete the pull requests. Tagging and publishing the release notes are handled by the **write-release-notes** playbook. Next, confirm the support-page listing (Stage 3). +This stage produces merge commits on `merge/*` branches but does not push or complete the pull requests. Tagging and publishing the release notes are handled by the **write-release-notes** playbook. Next, confirm the support-page listing (Stage 7). diff --git a/.github/agents/release-manager/validate-release/references/stage-3-support-page.md b/.github/agents/release-manager/validate-release/references/stage-7-support-page.md similarity index 89% rename from .github/agents/release-manager/validate-release/references/stage-3-support-page.md rename to .github/agents/release-manager/validate-release/references/stage-7-support-page.md index 72bedd2a617..b244a695b6f 100644 --- a/.github/agents/release-manager/validate-release/references/stage-3-support-page.md +++ b/.github/agents/release-manager/validate-release/references/stage-7-support-page.md @@ -1,4 +1,4 @@ -# Stage 3 - Confirm the Support-Page Update +# Stage 7 - Confirm the Support-Page Update The [.NET Platform Extensions support policy page](https://dotnet.microsoft.com/en-us/platform/support/policy/extensions) lists the supported packages and their current versions. It is maintained by a **partner team**, so this stage is a **review**, not an edit: confirm their update lands and is complete. @@ -17,4 +17,4 @@ The [.NET Platform Extensions support policy page](https://dotnet.microsoft.com/ ## After the stage -This is the final validation step. The release is complete once its symbols are public (Stage 1), the branches are reconciled (Stage 2), and the support-page listing is confirmed. +This is the final validation step. The release is complete once its symbols are public (Stage 5), the branches are reconciled (Stage 6), and the support-page listing is confirmed. diff --git a/.github/agents/release-manager/validate-release/scripts/Test-SourceLink.ps1 b/.github/agents/release-manager/validate-release/scripts/Test-SourceLink.ps1 index 68ad8ec3d06..e944c0c0815 100644 --- a/.github/agents/release-manager/validate-release/scripts/Test-SourceLink.ps1 +++ b/.github/agents/release-manager/validate-release/scripts/Test-SourceLink.ps1 @@ -20,14 +20,17 @@ #> param( [string]$PackageDir = '.', - [string]$SymbolServer = 'https://symbols.nuget.org/download/symbols/', + [string]$SymbolServer = 'https://msdl.microsoft.com/download/symbols/', [int]$Max = 0 # 0 = all packages ) $ErrorActionPreference = 'Continue' -$env:PATH += ';' + (Join-Path $env:USERPROFILE '.dotnet\tools') +$dotnetTools = Join-Path $HOME '.dotnet/tools' +if (Test-Path $dotnetTools) { + $env:PATH = "$env:PATH$([IO.Path]::PathSeparator)$dotnetTools" +} -$tmp = Join-Path $env:TEMP ('srclink-' + [guid]::NewGuid().ToString('N')) +$tmp = Join-Path ([IO.Path]::GetTempPath()) ('srclink-' + [guid]::NewGuid().ToString('N')) New-Item -ItemType Directory -Path $tmp | Out-Null $pkgs = Get-ChildItem -Path $PackageDir -Filter '*.nupkg' diff --git a/.github/agents/release-manager/write-release-notes/references/categorize-entries.md b/.github/agents/release-manager/write-release-notes/references/categorize-entries.md index e302bd6a5ff..7edc0f81d7b 100644 --- a/.github/agents/release-manager/write-release-notes/references/categorize-entries.md +++ b/.github/agents/release-manager/write-release-notes/references/categorize-entries.md @@ -11,7 +11,7 @@ For each candidate PR, assign one of these categories based on the primary inten | What's Changed | `changed` | Features, bug fixes, API improvements, performance, breaking changes | | Documentation Updates | `docs` | PRs whose sole purpose is documentation | | Test Improvements | `tests` | Adding, fixing, or improving tests | -| Repository Infrastructure Updates | `infra` | CI/CD, dependency bumps, version bumps, build system, agents and skills | +| Repository Infrastructure Updates | `infra` | CI/CD, dependency bumps, version bumps, build system, agents, workflows and skills | **Decision rules:** - If a PR modifies files under `src/Libraries/` or `src/Generators/` or `src/Analyzers/`, it is `changed` (even if it also touches docs or tests) From d95a7986dedaaf7c419ad74ef1c681565b2ad2b0 Mon Sep 17 00:00:00 2001 From: Jeff Handley Date: Mon, 20 Jul 2026 17:06:43 -0700 Subject: [PATCH 13/15] Add servicing release workflow to release-manager agent - Add servicing-release-prep.md playbook covering candidate selection, cherry-pick workflow, PR template with clean/conflict notation, and DO-NOT-SQUASH + rebase-and-merge guidance - Add Stage 3 build references for monthly and servicing tracks (stage-3-build-monthly.md, stage-3-build-servicing.md); rename from stage-3-land.md - Update publish/validate/write-release-notes to be track-aware: scope anchored to servicing-prep PR description for servicing releases - Add starter prompts and session-state assessment guidance to agent - Standardize terminology: 'monthly release' and 'servicing release' throughout; remove 'shipping build' in favor of 'official release build' Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: d88cce81-2b82-459a-a747-f456ea15a180 --- .github/agents/release-manager.agent.md | 54 ++++-- .../release-manager/prepare-release/README.md | 33 +++- .../references/servicing-release-prep.md | 159 ++++++++++++++++++ .../stage-1-prepare-internal-branch.md | 2 + .../references/stage-2-update-dependencies.md | 6 +- .../release-manager/publish-release/README.md | 20 ++- ...age-3-land.md => stage-3-build-monthly.md} | 4 +- .../references/stage-3-build-servicing.md | 35 ++++ .../references/stage-4-publish-and-promote.md | 108 ++++++++++-- .../validate-release/README.md | 12 +- .../references/stage-5-verify-source-link.md | 13 +- .../references/stage-6-reconcile-branches.md | 2 + .../write-release-notes/README.md | 7 +- .../references/categorize-entries.md | 2 +- .../references/format-template.md | 2 +- 15 files changed, 403 insertions(+), 56 deletions(-) create mode 100644 .github/agents/release-manager/prepare-release/references/servicing-release-prep.md rename .github/agents/release-manager/publish-release/references/{stage-3-land.md => stage-3-build-monthly.md} (94%) create mode 100644 .github/agents/release-manager/publish-release/references/stage-3-build-servicing.md diff --git a/.github/agents/release-manager.agent.md b/.github/agents/release-manager.agent.md index 665365b3702..e5c5d315f67 100644 --- a/.github/agents/release-manager.agent.md +++ b/.github/agents/release-manager.agent.md @@ -2,20 +2,25 @@ name: release-manager description: > Owns the end-to-end dotnet/extensions monthly and servicing release process across four playbooks: - prepare-release (stage the internal branch, update .NET dependencies), publish-release (land the - branch, publish packages to nuget.org, promote the shipping build to the public channel so symbols - reach msdl), validate-release (verify Source Link/symbols on msdl, reconcile the internal/public/main - branches), and write-release-notes (draft GitHub release notes for a tag). + prepare-release (monthly release prep: stage internal branch + dependency updates; servicing prep: + backport selected main commits onto release/* and bump patch version), publish-release (monthly release: + land to internal/release + publish/promote; servicing: run official build from release/* then publish), + validate-release (verify Source Link/symbols on msdl, reconcile branches when applicable), and + write-release-notes (draft GitHub release notes for a tag). USE FOR: "prepare for a release", "prepare internal release branch", "stage the release", - "update release dependencies", "validate/land the release branch", "publish the release", - "promote the build to the public channel", "fix missing/public symbols for a release", - "reconcile release branches", "write/draft release notes for ", and other dotnet/extensions - release operations. + "prepare a servicing release", "choose servicing backports", "update release dependencies", + "validate/land the release branch", "publish the release", "promote the official release build to the public channel", + "fix missing/public symbols for a release", "reconcile release branches", + "write/draft release notes for ", and other dotnet/extensions release operations. RECOMMENDED STARTER PROMPTS: "Where are we in the release process?", "Explain the dotnet/extensions release process to me.", "Help me prepare a dotnet/extensions release branch.", - "Help me update dependencies on an already prepared release branch.", "Help me land and publish a - prepared dotnet/extensions release.", "Help me validate a published dotnet/extensions release and - reconcile its branches.", or "Help me draft release notes for a dotnet/extensions release tag." + "Help me prepare a .. servicing release.", + "Help me choose which main commits to backport into release/..", + "Help me update dependencies on an already prepared release branch.", + "Help me land and publish a prepared dotnet/extensions release.", + "Help me publish a prepared servicing release from release/..", + "Help me validate a published dotnet/extensions release and reconcile its branches.", + or "Help me draft release notes for a dotnet/extensions release tag." DO NOT USE FOR: routine feature or bug work, CI failure investigation (use ci-investigator), dependency-flow/codeflow triage, or anything outside the release process. --- @@ -35,6 +40,10 @@ identify what is complete and what remains; and state any missing context. Earli happened in another session. Do not make changes while assessing status. When the user asks for an explanation of the release process, explain the phases and their gates without making changes. +When the user asks for a **servicing** release (patch release, e.g. `..`), use the +servicing release flow: prepare directly on `release/.`, pick backports from `main`, and avoid +the internal-branch prep/dependency-update flow used for monthly releases. + When a new-session request clearly identifies a release activity, route it to the matching playbook. When the user appears unsure how to begin -- for example, they ask for general release guidance, use a vague request such as "help with a release," or do not identify a release activity -- do not assume a @@ -44,8 +53,11 @@ these recommended starter prompts for the user to choose or adapt: - "Where are we in the release process?" - "Explain the dotnet/extensions release process to me." - "Help me prepare a dotnet/extensions release branch." +- "Help me prepare a .. servicing release." +- "Help me choose which main commits to backport into release/.." - "Help me update dependencies on an already prepared release branch." - "Help me land and publish a prepared dotnet/extensions release." +- "Help me publish a prepared servicing release from release/.." - "Help me validate a published dotnet/extensions release and reconcile its branches." - "Help me draft release notes for a dotnet/extensions release tag." @@ -58,8 +70,8 @@ reference file **only when you reach it** (progressive disclosure -- do not prel | The user wants to... | Playbook | Follow | |---|---|---| -| Prepare the release content (branch prep, dependency updates) | **prepare-release** | [release-manager/prepare-release/README.md](release-manager/prepare-release/README.md) | -| Ship the release (land, publish to nuget.org, promote to the public channel) | **publish-release** | [release-manager/publish-release/README.md](release-manager/publish-release/README.md) | +| Prepare the release content (monthly release prep or servicing prep) | **prepare-release** | [release-manager/prepare-release/README.md](release-manager/prepare-release/README.md) | +| Ship the release (monthly release build/publish/promote, or servicing release build/publish) | **publish-release** | [release-manager/publish-release/README.md](release-manager/publish-release/README.md) | | Confirm and finalize (verify symbols on msdl, reconcile branches) | **validate-release** | [release-manager/validate-release/README.md](release-manager/validate-release/README.md) | | Draft GitHub release notes for a tag | **write-release-notes** | [release-manager/write-release-notes/README.md](release-manager/write-release-notes/README.md) | @@ -88,10 +100,16 @@ verification script lives at `release-manager/validate-release/scripts/Test-Sour ## Release process at a glance -The release runs as three playbooks in order, then release notes: +There are two supported release tracks: + +1. **Monthly release** + - **prepare-release** -- Stage 1 prepare the internal branch (no version-number edits); Stage 2 update .NET 9, then .NET 8, then .NET 10 dependencies (three sub-stages). + - **publish-release** -- Stage 3 build from `internal/release/.` (gated on a green official build); Stage 4 publish to nuget.org and **promote the official release build to the public `.NET ` channel**. + - **validate-release** -- Stage 5 verify Source Link/symbols on msdl; Stage 6 reconcile internal -> public `release/.` -> `main`; Stage 7 confirm the support-page update. -1. **prepare-release** -- Stage 1 prepare the internal branch (no version-number edits); Stage 2 update .NET 9, then .NET 8, then .NET 10 dependencies (three sub-stages). Commit-producing preparation. -2. **publish-release** -- Stage 3 land on `internal/release/.` (gated on a green official build); Stage 4 publish to nuget.org and **promote the shipping build to the public `.NET ` channel** (required -- this is what puts symbols on msdl). Operational and irreversible. -3. **validate-release** -- Stage 5 verify Source Link/symbols on msdl until every library package is `valid` (the release sign-off gate); Stage 6 reconcile internal -> public `release/.` -> `main`; Stage 7 confirm the partner team's support-page update. +2. **Servicing release** + - **prepare-release** -- prepare directly on `release/.`: choose backports from `main`, bump patch version, and open a "Prepare .. Servicing Release" PR. + - **publish-release** -- after that PR merges and mirrors to AzDO, run `extensions-ci-official` from `release/.` and publish the selected package scope. + - **validate-release** -- run Source Link verification and post-release checks with the servicing package scope; run reconciliation only when explicitly needed. -Tagging and publishing the GitHub release notes are handled by the **write-release-notes** playbook. +Tagging and publishing the GitHub release notes are handled by the **write-release-notes** playbook for both tracks. diff --git a/.github/agents/release-manager/prepare-release/README.md b/.github/agents/release-manager/prepare-release/README.md index bf2c8bc350f..c42b4c7c9dd 100644 --- a/.github/agents/release-manager/prepare-release/README.md +++ b/.github/agents/release-manager/prepare-release/README.md @@ -1,10 +1,22 @@ # Prepare Release -Prepares a `dotnet/extensions` public release branch (`release/.`) for the internal release process on the corresponding `internal/release/.` branch. +Prepares a `dotnet/extensions` release for publication. This playbook supports **two preparation tracks**: -The preparation is organized into ordered stages. Work through them in sequence, loading each stage's reference file only when you reach that stage. +- **Monthly release** -- prepare a public branch (`release/.`) for the internal release flow on `internal/release/.`. +- **Servicing release** -- prepare directly on `release/.` by selecting backports from `main` and bumping patch version. -## Stage workflow +## Choose the preparation track first + +Before making changes, determine which track applies: + +| Release type | Use this path | +|---|---| +| Monthly release | Stages 1-2 in this README | +| Servicing release (`..`) | [references/servicing-release-prep.md](references/servicing-release-prep.md) | + +Do **not** mix tracks. Servicing releases should not run Stage 1/2 internal-branch prep unless the user explicitly asks to override the servicing workflow. + +## Stage workflow (monthly release) Complete stages strictly in order. Treat each stage -- and each sub-stage of a stage that has them -- as an independent, committable unit. For every stage or sub-stage: @@ -17,18 +29,27 @@ Every stage gets its own commit, and every sub-stage gets its own commit. Never Commits this playbook creates land in public dotnet/extensions history -- they flow out later through the non-squash internal-to-public merge (validate-release, Stage 6). Keep the `Co-authored-by: Copilot` trailer on those commits but omit the `Copilot-Session` trailer. -## Stage 1 - Prepare Internal Branch +## Stage 1 - Prepare Internal Branch (monthly release) Apply the internal-release infrastructure changes to the branch: suppress `NU1507`, remove the NuGet package source mapping, switch on stable/release versioning, add private-feed credential setup to the build template, comment out integration tests, and remove the code-coverage pipeline stage. Never change version numbers here -- those flow via Dependency Flow automation. Read and follow [references/stage-1-prepare-internal-branch.md](references/stage-1-prepare-internal-branch.md). -## Stage 2 - Update Dependencies +## Stage 2 - Update Dependencies (monthly release) Update the branch's product dependencies to the pending .NET 9, .NET 8, and .NET 10 servicing releases using `darc update-dependencies`. The BAR build IDs come from the release.dot.net Release Tracker, which is behind Microsoft auth and unreachable by the agent, so the user supplies them (pasted `ReleaseManifest.json` or a downloaded copy). This stage has three sub-stages, each its own commit: .NET 9, then .NET 8, then .NET 10. Read and follow [references/stage-2-update-dependencies.md](references/stage-2-update-dependencies.md). +## Servicing preparation workflow (servicing release) + +For servicing releases, do not use Stage 1/2 above. Instead, follow: + +- [references/servicing-release-prep.md](references/servicing-release-prep.md) + +That flow covers commit selection from `main`, patch-version bumping, servicing PR composition, and package-scope confirmation for later publish/validate/release-notes stages. + ## Next -Preparation is complete once Stages 1-2 are committed and reviewed. The release is then shipped and finalized by the sibling playbooks: **publish-release** (land, publish, promote) and **validate-release** (verify symbols, reconcile branches). +- Monthly release: once Stages 1-2 are committed and reviewed, continue with **publish-release** (land, publish, promote) and **validate-release** (verify symbols, reconcile branches). +- Servicing release: once the servicing prep PR merges into `release/.`, continue with **publish-release** (mirror + official build + publish selected scope), then **validate-release** and **write-release-notes**. diff --git a/.github/agents/release-manager/prepare-release/references/servicing-release-prep.md b/.github/agents/release-manager/prepare-release/references/servicing-release-prep.md new file mode 100644 index 00000000000..44091c895f8 --- /dev/null +++ b/.github/agents/release-manager/prepare-release/references/servicing-release-prep.md @@ -0,0 +1,159 @@ +# Servicing Release Preparation (Patch Releases) + +Use this flow for patch releases (`..`) prepared directly on the latest public `release/.` branch. + +This is a **different track** from Stage 1/2 internal-branch prep. Do not mix them unless the user explicitly overrides. + +## Outcome + +Produce a servicing-prep PR into `release/.` that includes: + +1. Patch-version bump commit. +2. Cherry-picks of user-selected `main` commits (in `main` merge order). +3. Any required release-branch-specific touch-ups. + +Then stop for merge/approval before publish steps. + +## Step 1: Confirm target branch and patch version + +1. Confirm the target public branch (`release/.`). +2. Confirm the target patch version (`..`). +3. Create a working branch from `origin/release/.`. + +## Step 2: Build the candidate backport list from `main` + +The user needs a clear **selection list** of commits from `main` that are not yet included in `release/.`. + +For grounding, also present a separate view of what is already merged into `release/.` (whether those merges happened before or after the target patch-version bump point). + +### 2a) Gather candidate PRs/commits + +Build candidates from commits in `main` that are not in `release/.`, then map them to PR numbers where possible. + +### 2b) Build grounding for already-merged release-branch items + +Identify items that already shipped on the release branch by checking merged PRs into `release/.` and harvesting referenced `#NNNN` slugs from their titles/bodies. + +Present these as **grounding only** (not selectable candidates). If a patch-bump commit for the target patch exists on the release branch, label each grounding row as: + +- `before patch bump`, or +- `after patch bump` + +If no patch-bump commit exists yet on the release branch, label rows as `before planned patch bump`. + +### 2c) Enrich each candidate with affected libraries + +For each candidate PR: + +1. Read changed files. +2. Derive affected libraries from touched paths (for example, `src/Libraries//...`). +3. Keep the list unique and sorted. + +### 2d) Present the selection table + +Show a selection table with one row per **selectable** candidate item: + +| SHA (short) | Title | PR | Libraries affected | +|---|---|---|---| +| `abc1234` | Commit/PR title | `#1234` link | `Microsoft.Extensions.AI`, ... | + +Then show a separate grounding table for already-merged release-branch items: + +| Timing vs patch bump | SHA (short) | Title | PR | Libraries affected | +|---|---|---|---|---| +| before patch bump / after patch bump / before planned patch bump | `def5678` | Commit/PR title | `#5678` link | `Microsoft.Extensions.AI.Abstractions`, ... | + +Requirements: + +- Keep already-merged release-branch rows out of the selectable table. +- Prefer PR-number rows when available. +- If a commit has no PR number, mark it and ask the user whether to include it explicitly. + +## Step 3: Ask the user to choose items (multi-select) + +Prompt the user to select rows from the **selectable candidate table only** (for example by PR numbers or row numbers, comma-separated). + +Do not cherry-pick until selection is explicit. + +## Step 4: Confirm package scope and template inclusion + +Before committing: + +1. Propose the package scope derived from selected items (default to coherent related package sets). +2. Ask the user to confirm or adjust that package scope. +3. If selected changes affect packages used by project templates, ask whether template packages should also be included in the servicing release. + +Record this confirmed scope; it is the source of truth for publish/validate/release-notes stages. + +## Step 5: Apply commits in servicing order + +Apply commits in this exact order: + +1. **Bump patch version first**. +2. **Cherry-pick selected commits** in the order they merged into `main`. +3. **Apply touch-ups** needed only for the servicing release branch. + +For each cherry-pick, record whether it was: + +- **Clean cherry-pick** (no merge conflicts), or +- **Cherry-pick with merge conflicts** (include conflict-resolution notes). + +### Patch version bump details + +- Update `eng/Versions.props` patch version for the target release. +- Commit message format: `Bump version to ..`. +- This matches recent 10.* servicing examples (`10.4.1`, `10.5.1`, `10.5.2`, `10.8.1`). + +## Step 6: Create the servicing-prep PR + +Do not push or open the PR until the user explicitly says to do so. + +When creating the PR into `release/.`, apply the `DO-NOT-SQUASH` label at creation time. + +PR title format: + +`Prepare .. Servicing Release` + +PR body format: + +```md +Prepares the .. servicing release for the following packages: +- () +- () +- () + +## Commits included +- Bump version to .. +- Clean cherry-pick # +- Cherry-pick with merge conflicts # + - Conflict: ; Resolution: + - Conflict: ; Resolution: +``` + +Notes: + +- Use `#` slugs for included PRs; GitHub renders number/title automatically. +- Keep commit order in the PR aligned with Step 5. +- For every conflicted cherry-pick, include one or more indented sub-bullets describing the conflict(s) and resolution(s). +- If a selected commit has no PR number, use its short SHA in place of `#`. +- Keep the `DO-NOT-SQUASH` label on the PR from creation through merge. +- For the merge method into `release/.`, prefer **Rebase and merge** (never squash). + +## Step 7: Preserve scope for downstream stages + +After PR creation, treat the merged servicing-prep PR description as authoritative for: + +- package publish scope (publish-release), +- package validation scope (validate-release), +- package scope in release notes (write-release-notes). + +If scope changes later, update the PR description and confirm with the user before publishing. + +## After this preparation + +After the servicing-prep PR merges into `release/.`, continue with **publish-release** servicing flow: + +1. wait for mirror into AzDO, +2. run `extensions-ci-official` from `release/.`, +3. publish selected packages, +4. continue post-release checks and notes. diff --git a/.github/agents/release-manager/prepare-release/references/stage-1-prepare-internal-branch.md b/.github/agents/release-manager/prepare-release/references/stage-1-prepare-internal-branch.md index 7587cf591ab..12f713f0f2b 100644 --- a/.github/agents/release-manager/prepare-release/references/stage-1-prepare-internal-branch.md +++ b/.github/agents/release-manager/prepare-release/references/stage-1-prepare-internal-branch.md @@ -1,5 +1,7 @@ # Stage 1 - Prepare Internal Branch +This stage is for the **monthly release**. Servicing releases should follow [servicing-release-prep.md](servicing-release-prep.md) instead. + When preparing a public branch for internal release, apply the following changes: ## 1. Directory.Build.props diff --git a/.github/agents/release-manager/prepare-release/references/stage-2-update-dependencies.md b/.github/agents/release-manager/prepare-release/references/stage-2-update-dependencies.md index c50da91666b..f8f950073e2 100644 --- a/.github/agents/release-manager/prepare-release/references/stage-2-update-dependencies.md +++ b/.github/agents/release-manager/prepare-release/references/stage-2-update-dependencies.md @@ -1,6 +1,8 @@ # Stage 2 - Update Dependencies -Update the internal release branch's product dependencies to the pending .NET 9, .NET 8, and .NET 10 servicing releases using `darc update-dependencies` against Build Asset Registry (BAR) build IDs. +This stage is for the **monthly release**. Servicing releases should follow [servicing-release-prep.md](servicing-release-prep.md) instead. + +Update the internal release branch's product dependencies to the pending .NET 9, .NET 8, and .NET 10 release updates using `darc update-dependencies` against Build Asset Registry (BAR) build IDs. This stage has three sub-stages. Run them in order, and give each its own commit: @@ -8,6 +10,8 @@ This stage has three sub-stages. Run them in order, and give each its own commit 2. Update Dependencies: .NET 8 3. Update Dependencies: .NET 10 +> **Why .NET 9 is first (this is intentional, not numeric order).** .NET 9 is the *coherent primary* band. `darc update-dependencies` and `eng/Version.Details.xml` write the **canonical, non-suffixed** version properties in `eng/Versions.props` (the `...Version` entries, which currently hold `9.0.x`), and only one band can own those canonical locations. So .NET 9 is applied first, on a clean tree, and its changes are kept wholesale -- `Version.Details.xml`, the non-suffixed `...Version` entries, and the base internal `NuGet.config` sources. .NET 8 (`...LTSVersion`, `8.0.x`) and .NET 10 (`...Net10Version`, `10.0.x`) are then layered on top as overlays: their darc writes to the canonical locations are reverted and the numbers hand-copied into the suffixed entries. That ordering is exactly why Sub-stages 2 and 3 tell you to *revert the 9.0 non-suffixed lines* and to keep the `NuGet.config` sources ".NET 9 added" -- those fix-up steps only make sense if .NET 9 has already run. + Commit each sub-stage automatically as you complete it -- do not pause for per-sub-stage review or approval. This overrides steps 2 and 3 of the [Stage workflow](../README.md#stage-workflow). Pushing remains a separate, user-directed step: do not push until the user explicitly instructs it (see "After the stage"). Before committing each sub-stage, review the diff yourself and revert any incidental, non-dependency changes darc introduces (for example, darc sometimes adds a trailing newline to `.config/dotnet-tools.json`). Keep only the intended dependency edits for that sub-stage. diff --git a/.github/agents/release-manager/publish-release/README.md b/.github/agents/release-manager/publish-release/README.md index b69c2845cae..0ae22e63698 100644 --- a/.github/agents/release-manager/publish-release/README.md +++ b/.github/agents/release-manager/publish-release/README.md @@ -1,21 +1,29 @@ # Publish Release -Ships a prepared `dotnet/extensions` release: land the prepared branch on `internal/release/.`, then publish the packages to nuget.org and promote the shipping build to the public channel so its symbols reach the Microsoft symbol server (msdl). +Publishes a prepared `dotnet/extensions` release. This playbook supports both tracks: + +- **Monthly release**: land the prepared branch on `internal/release/.`, then publish and promote. +- **Servicing release**: after servicing-prep PR merge, wait for mirror, run official build from `release/.`, then publish selected packages. Run this playbook after **prepare-release** is complete. Both stages are **operational** -- they push, queue pipelines, publish, and promote rather than producing ordinary commits -- and every push, publish, and promotion is **irreversible**. Run each stage only on explicit user instruction, and pause for confirmation before every irreversible action. -## Stage 3 - Land +## Stage 3 - Build -Land the prepared `stage-release-.` branch on `internal/release/.`, gated on a green official build, then run the official build on the release branch that produces the `PackageArtifacts`. Never auto-complete a pull request that needs elevation. +Stage 3 has two variants: -Read and follow [references/stage-3-land.md](references/stage-3-land.md). +- **Monthly release**: land the prepared `stage-release-.` branch on `internal/release/.`, then run the official build that produces `PackageArtifacts`. + Follow [references/stage-3-build-monthly.md](references/stage-3-build-monthly.md). +- **Servicing release**: after the servicing-prep PR merges into `release/.`, wait for mirror and run `extensions-ci-official` from that public release branch. + Follow [references/stage-3-build-servicing.md](references/stage-3-build-servicing.md). ## Stage 4 - Publish and Promote -Publish the packages to nuget.org (the user runs `dotnet nuget push`; the agent never handles API keys), then promote the shipping build to the public `.NET ` channel. The internal build is auto-assigned only to the `.NET Internal` channel (whose symbols go to an internal isolated feed), so promoting it to the public `.NET ` channel is a **required manual step** -- without it the shipped packages have no public symbols. The agent runs the promotion only after the user confirms the BAR id and target channel (irreversible once packages flow). +Publish the packages to nuget.org (the user runs `dotnet nuget push`; the agent never handles API keys), then ensure the official release build is on the public `.NET ` channel so symbols reach msdl. + +For servicing releases, use the merged servicing-prep PR description as the source of truth for package scope (what to publish and what to exclude) unless the user explicitly changes it. Read and follow [references/stage-4-publish-and-promote.md](references/stage-4-publish-and-promote.md). ## Next -Once the packages are published and the build promoted, run **validate-release** to verify the symbols on msdl and reconcile the branches. +Once the packages are published and channel assignment is confirmed, run **validate-release** to verify symbols on msdl and complete post-release checks. diff --git a/.github/agents/release-manager/publish-release/references/stage-3-land.md b/.github/agents/release-manager/publish-release/references/stage-3-build-monthly.md similarity index 94% rename from .github/agents/release-manager/publish-release/references/stage-3-land.md rename to .github/agents/release-manager/publish-release/references/stage-3-build-monthly.md index 44471ce191c..84b2e22c7ef 100644 --- a/.github/agents/release-manager/publish-release/references/stage-3-land.md +++ b/.github/agents/release-manager/publish-release/references/stage-3-build-monthly.md @@ -1,7 +1,9 @@ -# Stage 3 - Land +# Stage 3 - Build (Monthly Release) After the prepare-release playbook is committed and reviewed (Prepare Internal Branch, then Update Dependencies), land the `stage-release-.` branch on `internal/release/.` -- gated on a green official build. +This Stage 3 reference is for the **monthly release**. Servicing releases use [stage-3-build-servicing.md](stage-3-build-servicing.md) instead. + This stage is operational, not file-editing: it pushes existing commits and queues pipelines rather than producing new commits. Because it pushes to the internal remote and can land changes irreversibly, it runs **only on explicit user instruction** and pauses for confirmation before every push, pipeline queue, and land action. It produces no commit of its own. ## Prerequisites diff --git a/.github/agents/release-manager/publish-release/references/stage-3-build-servicing.md b/.github/agents/release-manager/publish-release/references/stage-3-build-servicing.md new file mode 100644 index 00000000000..d1d2f75b8ac --- /dev/null +++ b/.github/agents/release-manager/publish-release/references/stage-3-build-servicing.md @@ -0,0 +1,35 @@ +# Stage 3 - Build (Servicing Release) + +Use this Stage 3 variant for servicing releases prepared directly on `release/.`. + +Unlike the monthly release flow, there is no `stage-release-*` -> `internal/release/*` landing step. The servicing-prep PR merges into the public release branch first, then that branch is mirrored to AzDO. + +## Prerequisites + +- The servicing-prep PR into `release/.` is merged. +- You know the package scope from that PR description. +- You have access to Azure DevOps and the `extensions-ci-official` pipeline. + +## Steps + +1. Confirm the merged commit on `release/.`. +2. Wait for that commit to mirror into AzDO's `dotnet-extensions` repository. +3. Trigger `extensions-ci-official` from `refs/heads/release/.` (or the mirrored equivalent branch ref). +4. Wait for a successful official run producing release `PackageArtifacts`. +5. Record and share with the user: + - AzDO build run URL + - AzDO build ID / build number + - BAR build ID for the official release build + +## Notes + +- This stage is operational and irreversible once publishing starts downstream. +- Do not proceed to Stage 4 publishing until the official run is green and artifact identity (commit + BAR id) is confirmed. +- Do not push/merge anything from this stage unless the user explicitly asks. + +## After the stage + +Proceed to **Stage 4 - Publish and Promote** using: + +- the official release build from this stage, and +- the servicing package scope captured in the merged servicing-prep PR description. diff --git a/.github/agents/release-manager/publish-release/references/stage-4-publish-and-promote.md b/.github/agents/release-manager/publish-release/references/stage-4-publish-and-promote.md index 5fced480d52..b5d51f12aa1 100644 --- a/.github/agents/release-manager/publish-release/references/stage-4-publish-and-promote.md +++ b/.github/agents/release-manager/publish-release/references/stage-4-publish-and-promote.md @@ -1,34 +1,120 @@ # Stage 4 - Publish and Promote -After the Land stage (publish-release Stage 3) lands the release on `internal/release/.` and its official build produces the `PackageArtifacts`, publish the packages to nuget.org and promote the shipping build to the public channel so its symbols publish to the Microsoft symbol server (msdl). +After Stage 3 produces the official release build (`PackageArtifacts`), publish the packages to nuget.org and ensure that build is assigned to the public channel so symbols publish to the Microsoft symbol server (msdl). -This stage is operational and produces no commit. Both actions are **irreversible**: publishing is human-gated (the agent prepares and reviews the package set but never runs `dotnet nuget push` itself and never handles API keys), and the channel promotion runs only after the user confirms. Verifying that the shipped symbols actually landed on msdl is the next playbook -- **validate-release**. +This stage is shared by both tracks: + +- **Monthly release**: Stage 3 official release build comes from `internal/release/.`. +- **Servicing release**: Stage 3 official release build comes from public `release/.`. + +This stage is operational and produces no commit. Both actions are **irreversible**: publishing is human-gated (the agent prepares and reviews the package set but never runs `dotnet nuget push` itself and never handles API keys), and the channel promotion runs only after the user confirms. Verifying that the published symbols actually landed on msdl is the next playbook -- **validate-release**. ## Prerequisites -- The Land stage is complete: the official build on `internal/release/.` succeeded and produced the `PackageArtifacts`. +- Stage 3 is complete: the official release build succeeded and produced the `PackageArtifacts`. - For the channel promotion: the `darc` CLI, authenticated to the Build Asset Registry (BAR). +For servicing releases, also require the merged servicing-prep PR description; it is the source of truth for package scope. + ## Sub-stage 1 - Prepare and publish packages Publishing is irreversible (a published version cannot be overwritten or truly deleted) and requires nuget.org API keys, so the agent only prepares the set; the user runs the push. 1. Download and extract the `PackageArtifacts` from the official build. -2. Stage the packages to publish into a clean folder, **excluding**: - - `Microsoft.Internal.*` (always). - - Any packages the user names to hold back for this release. Confirm the exact list, and flag template/tooling packages (for example `*.ProjectTemplates`) for an explicit decision. +2. Stage the packages to publish into a clean folder: + - Always exclude `Microsoft.Internal.*`. + - For **servicing releases**, start from the package list in the merged servicing-prep PR description; treat that list as canonical unless the user explicitly changes it. + - For **monthly releases**, include all release packages except those the user explicitly holds back. + - In both tracks, flag template/tooling packages (for example `*.ProjectTemplates`) for an explicit include/exclude decision. 3. Present the excluded and to-publish lists for the user to review. 4. Two nuget.org accounts are involved: almost all packages publish from the **dotnetframework** account; **`Microsoft.Agents.AI.ProjectTemplates`** publishes from the **MicrosoftAgentFramework** account, so it must be pushed separately with that account's key. 5. The **user** runs `dotnet nuget push` with the appropriate API key(s). Never run the push, and never handle the API keys. -## Sub-stage 2 - Promote the shipping build to the public channel +### Secure API key entry (user-run helper) + +The API key must never be pasted into the agent conversation (it would be captured in session history) and the agent must never run the push or hold the key in a process it controls. Instead, the **user** runs a self-contained helper in their own terminal that captures the key through an editor, pushes the staged packages, then shreds the key file. The key is never echoed and never leaves the user's machine. + +The helper opens a throwaway key file in the user's editor with a **wait** flag (so the command blocks until the file is saved and closed), reads the key after close, pushes each staged package, then overwrites and deletes the key file in a `finally` block (so cleanup runs even on error or Ctrl-C). Editors are launched with the flag that blocks until the file is closed: + +| Editor | Invocation | +|---|---| +| Sublime Text | `subl --wait ` | +| VS Code | `code --wait -- ` | +| Notepad | `Start-Process notepad.exe -ArgumentList -Wait` | + +```powershell +#requires -Version 5.1 +[CmdletBinding()] +param( + # Absolute paths to the staged .nupkg files to publish. + [Parameter(Mandatory)][string[]] $Package, + # Editor used to capture the key. Default is Sublime Text. + [ValidateSet('subl','code','notepad')][string] $Editor = 'subl', + [string] $Source = 'https://api.nuget.org/v3/index.json' +) + +$ErrorActionPreference = 'Stop' +$key = $null +$keyFile = Join-Path ([System.IO.Path]::GetTempPath()) ("nugetkey_{0}.txt" -f ([guid]::NewGuid().ToString('N'))) +Set-Content -LiteralPath $keyFile -Value '' -NoNewline + +try { + Write-Host "Opening '$Editor'. Paste the nuget.org API key, save, then CLOSE the file to continue..." + switch ($Editor) { + 'subl' { & subl --wait $keyFile } + 'code' { & code --wait -- $keyFile } + 'notepad' { Start-Process -FilePath notepad.exe -ArgumentList $keyFile -Wait } + } + + $key = ([string](Get-Content -LiteralPath $keyFile -Raw)).Trim() + if ([string]::IsNullOrWhiteSpace($key)) { throw "No API key was entered; aborting (nothing pushed)." } + + foreach ($p in $Package) { + if (-not (Test-Path -LiteralPath $p)) { throw "Package not found: $p" } + Write-Host "Pushing $([System.IO.Path]::GetFileName($p)) ..." + dotnet nuget push $p --source $Source --api-key $key + if ($LASTEXITCODE -ne 0) { throw "push failed for '$p' (exit $LASTEXITCODE)" } + } + Write-Host "All packages pushed." +} +finally { + # Overwrite the key file with random bytes, then delete it. Runs even on error/Ctrl-C. + if (Test-Path -LiteralPath $keyFile) { + try { + $len = [int](Get-Item -LiteralPath $keyFile).Length + if ($len -gt 0) { + $rng = [System.Security.Cryptography.RandomNumberGenerator]::Create() + $rand = New-Object byte[] $len + $rng.GetBytes($rand); $rng.Dispose() + [System.IO.File]::WriteAllBytes($keyFile, $rand) + } + } catch { } + Remove-Item -LiteralPath $keyFile -Force -ErrorAction SilentlyContinue + } + if ($key) { $key = $null; Remove-Variable key -ErrorAction SilentlyContinue } +} +``` + +Notes: +- Pass the staged `.nupkg` paths via `-Package`; select the editor with `-Editor subl|code|notepad` (default `subl`, matching this repository's release engineer's preference). +- The key is passed to `dotnet nuget push` as `--api-key`, so it is briefly visible in local process listings for the duration of each push -- acceptable on the release engineer's own machine. To avoid even that exposure, upload the `.nupkg` files through the nuget.org web UI instead. +- `Microsoft.Agents.AI.ProjectTemplates` uses the separate **MicrosoftAgentFramework** account key; when it is in scope, run the helper a second time with only that package so its key is entered separately. + +After a successful push, **delete or regenerate the API key on nuget.org** ([nuget.org/account/apikeys](https://www.nuget.org/account/apikeys)) so the key used for this release is not left valid. The helper shreds the temporary key *file* locally, but only deleting/regenerating the key on nuget.org invalidates it server-side. Guide the user to do this as the final step of Sub-stage 1. + +## Sub-stage 2 - Ensure the official release build is on the public channel + +Determine whether manual promotion is required: -The packages you just published to nuget.org were built by the official build on `internal/release/.`. A darc **default-channel** rule auto-assigns that build to the **`.NET Internal`** channel, whose symbols publish to an internal isolated feed -- **not** the public Microsoft symbol server (msdl). The public **`.NET `** channel (which does publish symbols to msdl) is only auto-assigned to builds from the *public* `release/.` branch, and **no subscription promotes the internal build to it**. So the shipping build's symbols reach msdl only after you **manually promote it** to the public channel. Skipping this leaves the shipped packages with no public symbols / Source Link -- the failure this sub-stage exists to prevent. +- **Monthly release builds from `internal/release/.`** are typically auto-assigned only to `.NET Internal`; manual promotion to `.NET ` is usually required. +- **Servicing builds from public `release/.`** are typically auto-assigned to `.NET ` already; verify before attempting promotion. -1. Identify the shipping build's **BAR id** -- the BAR build for the official `internal/release/.` build whose artifacts you published. Confirm its commit matches the `repository/commit` embedded in a shipped `.nuspec`. `darc get-build --id ` should show it on `.NET Internal` and typically **not** yet on `.NET `. +1. Identify the official release build's **BAR id** and confirm its commit matches the `repository/commit` embedded in a published `.nuspec`. 2. Find the public channel id: `darc get-channels` -> the entry named exactly `.NET ` (no `Internal`/`Eng`/`Private`/`Workload` suffix). -3. Promote the build to the public channel with `darc add-build-to-channel --id --channel ".NET "` -- **confirm the BAR id and channel with the user before triggering**, since this publishes its symbols to msdl (`SymbolTargetType: Public`) **and** flows its packages downstream to that channel, and is **irreversible once packages flow**. A non-blocking "Build validation audit failure for production channel" warning is expected. -4. Wait for the promotion build to complete, then confirm: `darc get-build --id ` now lists `.NET ` among its channels. +3. Check current channel assignment with `darc get-build --id `: + - If `.NET ` is already present, record that and skip manual promotion. + - If `.NET ` is absent, promote with `darc add-build-to-channel --id --channel ".NET "` **only after explicit user confirmation**. +4. If promotion was required, wait for it to complete, then confirm `.NET ` appears in `darc get-build --id `. ## After the stage diff --git a/.github/agents/release-manager/validate-release/README.md b/.github/agents/release-manager/validate-release/README.md index d7bcb273e55..77ac7938429 100644 --- a/.github/agents/release-manager/validate-release/README.md +++ b/.github/agents/release-manager/validate-release/README.md @@ -1,12 +1,18 @@ # Validate Release -Confirms a published `dotnet/extensions` release is correct and finalizes it: verify that the shipped symbols are on the Microsoft symbol server (msdl) with working Source Link, then reconcile the internal, public, and `main` branches. +Confirms a published `dotnet/extensions` release is correct and finalizes it: verify that the published symbols are on the Microsoft symbol server (msdl) with working Source Link, then reconcile the internal, public, and `main` branches. -Run this playbook after **publish-release**. Stage 5 is automated symbol verification; Stage 6 stages the reconciliation merges (pushing and PR completion are left to the user); Stage 7 confirms the support-page listing. +Run this playbook after **publish-release**. + +- Stage 5 is automated symbol verification. +- Stage 6 stages reconciliation merges when needed (pushing and PR completion are left to the user). +- Stage 7 confirms the support-page listing. + +For servicing releases prepared directly on public `release/.`, Stage 6 is often unnecessary because commits were backported from `main` into the release branch up front. In that case, run Stage 5 and Stage 7, and run Stage 6 only if the user explicitly asks for additional branch-flow follow-up. ## Stage 5 - Verify Source Link and Symbols -Run the Source Link sweep against the published packages until every library package reports `valid` on msdl. This is the **release sign-off gate** -- a persistent `symbols-not-indexed` result means the shipping build never reached the public `.NET ` channel (see publish-release, Stage 4). +Run the Source Link sweep against the published packages until every library package reports `valid` on msdl. This is the **release sign-off gate** -- a persistent `symbols-not-indexed` result means the official release build never reached the public `.NET ` channel (see publish-release, Stage 4). Read and follow [references/stage-5-verify-source-link.md](references/stage-5-verify-source-link.md). diff --git a/.github/agents/release-manager/validate-release/references/stage-5-verify-source-link.md b/.github/agents/release-manager/validate-release/references/stage-5-verify-source-link.md index 35a3aa0a3f0..a779e77a1e4 100644 --- a/.github/agents/release-manager/validate-release/references/stage-5-verify-source-link.md +++ b/.github/agents/release-manager/validate-release/references/stage-5-verify-source-link.md @@ -1,15 +1,18 @@ # Stage 5 - Verify Source Link and Symbols -The publish-release playbook published the packages to nuget.org and promoted the shipping build to the public `.NET ` channel. Only after that promotion do the shipped symbols publish to the Microsoft symbol server (msdl). Verify Source Link and symbol-server availability -- this is the **release sign-off gate**. +The publish-release playbook published the packages to nuget.org and promoted/assigned the official release build to the public `.NET ` channel. Only after that assignment do symbols publish to the Microsoft symbol server (msdl). Verify Source Link and symbol-server availability -- this is the **release sign-off gate**. ## Prerequisites -- publish-release is complete: packages published to nuget.org and the shipping build promoted to the public `.NET ` channel. +- publish-release is complete: packages published to nuget.org and the official release build assigned/promoted to the public `.NET ` channel. - The `sourcelink` and `dotnet-symbol` global tools (`dotnet tool install -g sourcelink`; `dotnet tool install -g dotnet-symbol`). +- The package scope for this release is known: + - Monthly release: the full release package set. + - Servicing release: the package list from the merged servicing-prep PR description (unless the user explicitly changed scope at publish time). ## Verify -Run the Source Link sweep against the folder of published packages: +Run the Source Link sweep against the folder containing the published packages for this release scope: ``` ./.github/agents/release-manager/validate-release/scripts/Test-SourceLink.ps1 -PackageDir @@ -22,9 +25,9 @@ For each `.nupkg` the script extracts a lib DLL, pulls the matching PDB from the - `symbols-not-indexed` -- the PDB is not yet on msdl (still "Validating..." on nuget.info; re-run later). - `no-lib-dll` -- template/tooling package with no `lib/**/*.dll` (expected). -Indexing on msdl lags the promotion, so `symbols-not-indexed` immediately afterward is expected -- **re-run until every library package is `valid`**. If packages stay `symbols-not-indexed` well after the promotion, re-confirm the shipping build is actually on the public `.NET ` channel (`darc get-build --id ` should list `.NET `); an internal-only build's symbols go to the internal isolated feed and never reach msdl. Investigate any `sourcelink-FAILED`. +Indexing on msdl lags the promotion/channel assignment, so `symbols-not-indexed` immediately afterward is expected -- **re-run until every published library package in scope is `valid`**. If packages stay `symbols-not-indexed` well after publish, re-confirm the official release build is actually on the public `.NET ` channel (`darc get-build --id ` should list `.NET `). Investigate any `sourcelink-FAILED`. -**Do not sign off the release until every library package reports `valid`.** +**Do not sign off the release until every published library package in scope reports `valid`.** ## After the stage diff --git a/.github/agents/release-manager/validate-release/references/stage-6-reconcile-branches.md b/.github/agents/release-manager/validate-release/references/stage-6-reconcile-branches.md index d5553e58070..80a238837cc 100644 --- a/.github/agents/release-manager/validate-release/references/stage-6-reconcile-branches.md +++ b/.github/agents/release-manager/validate-release/references/stage-6-reconcile-branches.md @@ -2,6 +2,8 @@ After Stage 5 verifies the published symbols, reconcile the branches: merge the internal release branch back out to the public release branch, then merge the public release branch into `main`. This is the final release activity. +This stage is designed for the **monthly release**. For servicing releases prepared directly on public `release/.`, Stage 6 is usually skipped unless the user explicitly asks for additional branch-flow reconciliation. + Like the publish-release stages, this stage is operational: it stages merges and creates commits on throwaway `merge/*` branches, but it does **not** push or complete pull requests on its own. The agent stages each merge, applies the required file "doctoring", commits, and shows the diff for review; **pushing and completing the PRs (which need JIT elevation / admin settings) are user-directed**, and each merge PR must land as a merge commit (never squashed). Run each sub-stage only when the user instructs it. Sub-stage 2 depends on Sub-stage 1's PR having already merged into the public release branch. diff --git a/.github/agents/release-manager/write-release-notes/README.md b/.github/agents/release-manager/write-release-notes/README.md index 963b2f22982..f3330d1927a 100644 --- a/.github/agents/release-manager/write-release-notes/README.md +++ b/.github/agents/release-manager/write-release-notes/README.md @@ -13,7 +13,7 @@ The `dotnet/extensions` repository ships NuGet packages across many functional a The repository does not follow Semantic Versioning. Major versions align with annual .NET releases, minor versions increment monthly, and patch versions are for intra-month fixes. -The repository makes heavy use of `[Experimental]` attributes. Experimental diagnostic IDs are documented in [`docs/list-of-diagnostics.md`](../../docs/list-of-diagnostics.md). Breaking changes to experimental APIs are expected and acceptable. Graduation of experimental APIs to stable is a noteworthy positive event. +The repository makes heavy use of `[Experimental]` attributes. Experimental diagnostic IDs are documented in [`docs/list-of-diagnostics.md`](../../../../docs/list-of-diagnostics.md). Breaking changes to experimental APIs are expected and acceptable. Graduation of experimental APIs to stable is a noteworthy positive event. The repository uses `release/` branches (e.g. `release/10.4`) where release tags are associated with commits on those branches. When determining the commit range for a release, ensure the previous and target tags are resolved against the appropriate release branch history. @@ -23,6 +23,7 @@ The repository uses `release/` branches (e.g. `release/10.4`) where release tags - **Do not run linters, formatters, or validators** on the output. - **Maximize parallel tool calls.** Fetch multiple PR and issue details in a single response. - **Package assignment is file-path-driven.** Determine which packages a PR affects by examining which `src/Libraries/{PackageName}/` paths it touches. See [references/package-areas.md](references/package-areas.md) for the mapping. Use `area-*` labels only as a fallback. +- **For servicing releases, use the servicing-prep PR description as scope input.** If a merged PR titled `Prepare .. Servicing Release` exists, treat its package list and commit list as the default source of truth for publish/validate/release-notes scope unless the user explicitly overrides it. ## Process @@ -38,7 +39,7 @@ The user may provide: Once the range is established: 1. Determine if this is a **full release** (minor version bump) or **patch release** (patch version bump) based on the version numbers. -2. For patch releases, ask the user which packages are included (or infer from the PRs). +2. For patch releases, ask the user which packages are included (or infer from the PRs). If a servicing-prep PR exists, start from its package list and confirm any overrides. 3. Get the merge date range for PR collection. ### Step 2: Collect and Enrich PRs @@ -77,7 +78,7 @@ Follow [references/experimental-features.md](references/experimental-features.md Build the package version information: 1. For **full releases**: all packages ship at the same version. Note the version number but do not generate a per-package table — it would be repetitive with no value. -2. For **patch releases**: build a table of only the affected packages and their version numbers. +2. For **patch releases**: build a table of only the affected packages and their version numbers. If a servicing-prep PR exists, seed this table from that PR's package list before applying user overrides. 3. Present the version information to the user for confirmation. The user may adjust which packages are included in a patch release. ### Step 6: Draft Release Notes diff --git a/.github/agents/release-manager/write-release-notes/references/categorize-entries.md b/.github/agents/release-manager/write-release-notes/references/categorize-entries.md index 7edc0f81d7b..237be07ab49 100644 --- a/.github/agents/release-manager/write-release-notes/references/categorize-entries.md +++ b/.github/agents/release-manager/write-release-notes/references/categorize-entries.md @@ -59,7 +59,7 @@ These categories are **not** grouped by package area. They appear as flat lists ## Full vs. patch release considerations -### Full monthly release +### Monthly release - All areas with changes get their own heading - All four category sections appear (omit empty ones) - Include the "Experimental API Changes" section if any experimental changes were detected diff --git a/.github/agents/release-manager/write-release-notes/references/format-template.md b/.github/agents/release-manager/write-release-notes/references/format-template.md index 93f4a18debd..3a50562ba4c 100644 --- a/.github/agents/release-manager/write-release-notes/references/format-template.md +++ b/.github/agents/release-manager/write-release-notes/references/format-template.md @@ -1,6 +1,6 @@ # Release Notes Format Template -## Full monthly release +## Monthly release Use this template when all packages ship together (e.g. v10.3.0 → v10.4.0). From af958169b5431458083e4bbf10fec960dd3e0a00 Mon Sep 17 00:00:00 2001 From: Jeff Handley Date: Mon, 20 Jul 2026 23:29:39 -0700 Subject: [PATCH 14/15] Clarify package coherency --- .../prepare-release/references/servicing-release-prep.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/agents/release-manager/prepare-release/references/servicing-release-prep.md b/.github/agents/release-manager/prepare-release/references/servicing-release-prep.md index 44091c895f8..a20a150fcb6 100644 --- a/.github/agents/release-manager/prepare-release/references/servicing-release-prep.md +++ b/.github/agents/release-manager/prepare-release/references/servicing-release-prep.md @@ -79,7 +79,7 @@ Do not cherry-pick until selection is explicit. Before committing: -1. Propose the package scope derived from selected items (default to coherent related package sets). +1. Propose the package scope derived from selected items (default to coherent related package sets). For example, when any of Microsoft.Extensions.AI, Microsoft.Extensions.AI.Abstractions, and Microsoft.Extensions.AI.OpenAI are updated, all three are released, but the Microsoft.Extensions.AI.Evaluation packages are not released unless they are also updated. 2. Ask the user to confirm or adjust that package scope. 3. If selected changes affect packages used by project templates, ask whether template packages should also be included in the servicing release. From 8d3d7e2524b95162011c96d74d0d44e289711b18 Mon Sep 17 00:00:00 2001 From: Jeff Handley Date: Thu, 23 Jul 2026 02:33:05 -0700 Subject: [PATCH 15/15] Rename servicing prep reference and update links Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d88cce81-2b82-459a-a747-f456ea15a180 --- .github/agents/release-manager/prepare-release/README.md | 4 ++-- .../references/stage-1-prepare-internal-branch.md | 2 +- .../prepare-release/references/stage-2-update-dependencies.md | 2 +- ...rvicing-release-prep.md => stages-1-2-servicing-branch.md} | 0 4 files changed, 4 insertions(+), 4 deletions(-) rename .github/agents/release-manager/prepare-release/references/{servicing-release-prep.md => stages-1-2-servicing-branch.md} (100%) diff --git a/.github/agents/release-manager/prepare-release/README.md b/.github/agents/release-manager/prepare-release/README.md index c42b4c7c9dd..1076206f1cc 100644 --- a/.github/agents/release-manager/prepare-release/README.md +++ b/.github/agents/release-manager/prepare-release/README.md @@ -12,7 +12,7 @@ Before making changes, determine which track applies: | Release type | Use this path | |---|---| | Monthly release | Stages 1-2 in this README | -| Servicing release (`..`) | [references/servicing-release-prep.md](references/servicing-release-prep.md) | +| Servicing release (`..`) | [references/stages-1-2-servicing-branch.md](references/stages-1-2-servicing-branch.md) | Do **not** mix tracks. Servicing releases should not run Stage 1/2 internal-branch prep unless the user explicitly asks to override the servicing workflow. @@ -45,7 +45,7 @@ Read and follow [references/stage-2-update-dependencies.md](references/stage-2-u For servicing releases, do not use Stage 1/2 above. Instead, follow: -- [references/servicing-release-prep.md](references/servicing-release-prep.md) +- [references/stages-1-2-servicing-branch.md](references/stages-1-2-servicing-branch.md) That flow covers commit selection from `main`, patch-version bumping, servicing PR composition, and package-scope confirmation for later publish/validate/release-notes stages. diff --git a/.github/agents/release-manager/prepare-release/references/stage-1-prepare-internal-branch.md b/.github/agents/release-manager/prepare-release/references/stage-1-prepare-internal-branch.md index 12f713f0f2b..51607010080 100644 --- a/.github/agents/release-manager/prepare-release/references/stage-1-prepare-internal-branch.md +++ b/.github/agents/release-manager/prepare-release/references/stage-1-prepare-internal-branch.md @@ -1,6 +1,6 @@ # Stage 1 - Prepare Internal Branch -This stage is for the **monthly release**. Servicing releases should follow [servicing-release-prep.md](servicing-release-prep.md) instead. +This stage is for the **monthly release**. Servicing releases should follow [stages-1-2-servicing-branch.md](stages-1-2-servicing-branch.md) instead. When preparing a public branch for internal release, apply the following changes: diff --git a/.github/agents/release-manager/prepare-release/references/stage-2-update-dependencies.md b/.github/agents/release-manager/prepare-release/references/stage-2-update-dependencies.md index f8f950073e2..f1d96d3d564 100644 --- a/.github/agents/release-manager/prepare-release/references/stage-2-update-dependencies.md +++ b/.github/agents/release-manager/prepare-release/references/stage-2-update-dependencies.md @@ -1,6 +1,6 @@ # Stage 2 - Update Dependencies -This stage is for the **monthly release**. Servicing releases should follow [servicing-release-prep.md](servicing-release-prep.md) instead. +This stage is for the **monthly release**. Servicing releases should follow [stages-1-2-servicing-branch.md](stages-1-2-servicing-branch.md) instead. Update the internal release branch's product dependencies to the pending .NET 9, .NET 8, and .NET 10 release updates using `darc update-dependencies` against Build Asset Registry (BAR) build IDs. diff --git a/.github/agents/release-manager/prepare-release/references/servicing-release-prep.md b/.github/agents/release-manager/prepare-release/references/stages-1-2-servicing-branch.md similarity index 100% rename from .github/agents/release-manager/prepare-release/references/servicing-release-prep.md rename to .github/agents/release-manager/prepare-release/references/stages-1-2-servicing-branch.md