documentation/release-checklist.md
Artifacts produced over the course of the release. Record each URL here as the corresponding phase completes so this issue serves as the single index back into every PR / build / tag that defines {{THIS_RELEASE_EXACT_VERSION}}.
| Artifact | URL |
|---|---|
Phase 1.2d — maestro-configuration PR (channels for {{THIS_RELEASE_VERSION}} / {{NEXT_VERSION}}) | {{URL_OF_PHASE1_DARC_PR}} |
Phase 2.3h — maestro-configuration PR (main subscriptions retargeted to VS {{NEXT_VERSION}}, retired-branch cleanup) | {{URL_OF_PHASE2_DARC_PR}} |
Phase 3.4 — main next-version main-bump PR | {{URL_OF_NEXT_VERSION_MAIN_BUMP_PR}} |
Phase 4.3 — vs{{THIS_RELEASE_VERSION}} final branding PR | {{URL_OF_FINAL_BRANDING_PR}} |
| Phase 4.6 — VS insertion PR | {{URL_OF_VS_INSERTION}} |
| Phase 5.3 — GitHub release tag | https://github.com/dotnet/msbuild/releases/tag/v{{THIS_RELEASE_EXACT_VERSION}} |
Fill in these values before starting. Version increments are irregular — they must be specified explicitly.
| Placeholder | Description | Value |
|---|---|---|
{{PREVIOUS_RELEASE_VERSION}} | Version being replaced as latest | |
{{THIS_RELEASE_VERSION}} | Version being released now | |
{{THIS_RELEASE_EXACT_VERSION}} | The VersionPrefix that actually shipped to customers — read it from VS rel/stable (see Phase 5.1a), not assumed. It is usually {{THIS_RELEASE_VERSION}}.0, but an OptProf-driven insertion bump can make the shipped version {{THIS_RELEASE_VERSION}}.1 or higher (e.g. 18.7 shipped as 18.7.1). Not known when first instantiating this checklist — leave blank until Phase 5.1a confirms it. | |
{{NEXT_VERSION}} | Version that main will be bumped to | |
{{BRANCH_SNAP_DATE}} | Date we create vs{{THIS_RELEASE_VERSION}} from main. | |
{{INSIDERS_SNAP_DATE}} | Date VS snaps main → rel/insiders. Final-branded MSBuild must be in VS main before this date. From VS-Dates wiki | |
{{STABLE_SNAP_DATE}} | Date VS snaps rel/insiders → rel/stable. From VS-Dates wiki | |
{{VS_SHIP_DATE}} | Date VS ships publicly (GA). Post-GA tasks (nuget.org, docs) happen after this. | |
{{PACKAGE_VALIDATION_BASELINE_VERSION}} | Latest {{THIS_RELEASE_VERSION}}.0-preview-NNNNN-NN MSBuild build reachable from vs{{THIS_RELEASE_VERSION}}. Used as the ApiCompat baseline for the bumped main. How to determine it: see the release skill. |
Derived values (do not edit — computed from inputs):
vs{{THIS_RELEASE_VERSION}}VS {{THIS_RELEASE_VERSION}}VS {{NEXT_VERSION}}main (VS snaps main → insiders → stable on its own schedule)Trigger: User decides to start a release. Do this first.
eng/Versions.props on main has VersionPrefix = {{THIS_RELEASE_VERSION}}.0 — if not, the inputs are wrongvs{{THIS_RELEASE_VERSION}} does not already exist — if it does, this release was already startedVS {{THIS_RELEASE_VERSION}} exists: darc get-channelsdarc add-channel --name "VS {{THIS_RELEASE_VERSION}}"{{PLACEHOLDERS}} replacedTrigger:
{{BRANCH_SNAP_DATE}}reached.
Steps are sequential — complete in order.
main that should ship in {{THIS_RELEASE_VERSION}}. Anything that lands in main after Phase 1.1 will go into {{NEXT_VERSION}} instead.vs{{THIS_RELEASE_VERSION}} from HEAD of main (requires repo admin rights — git push to refs/heads/vs* is restricted; if you don't have permission, ask a repo admin with vs* push rights to do it): git push upstream HEAD:refs/heads/vs{{THIS_RELEASE_VERSION}}
git push upstream <correct_sha>:refs/heads/vs{{THIS_RELEASE_VERSION}}--configuration-branch msbuild-{{THIS_RELEASE_VERSION}} on every command and --no-pr on all but the last:
darc get-default-channels --channel "VS {{THIS_RELEASE_VERSION}}" --branch vs{{THIS_RELEASE_VERSION}} --source-repo https://github.com/dotnet/msbuild No matching channels were found.: darc add-default-channel --channel "VS {{THIS_RELEASE_VERSION}}" --branch vs{{THIS_RELEASE_VERSION}} --repo https://github.com/dotnet/msbuild --configuration-branch msbuild-{{THIS_RELEASE_VERSION}} --no-prdarc add-channel --name "VS {{NEXT_VERSION}}" --configuration-branch msbuild-{{THIS_RELEASE_VERSION}} --no-pr --no-pr to create the PR): darc add-default-channel --channel "VS {{NEXT_VERSION}}" --branch vs{{NEXT_VERSION}} --repo https://github.com/dotnet/msbuild --configuration-branch msbuild-{{THIS_RELEASE_VERSION}}.config/git-merge-flow-config.jsonc:
vs{{THIS_RELEASE_VERSION}} as the last entry before main in the merge chain. Add a comment noting the VS/SDK version context.MergeToBranch entries and rewire the chain to skip them so automation does not open stale forward-merge PRs. vs{{THIS_RELEASE_VERSION}} - 3 — always confirm on the wiki, since servicing exceptions can extend specific versions beyond the rule of thumb.Trigger:
vs{{THIS_RELEASE_VERSION}}branch exists (Phase 1 complete).
Why this runs before bumpingmain: consumers of MSBuild viamain(notably the VMR) should start receiving next-version bits from theVS {{NEXT_VERSION}}channel the momentmainis bumped. Reassigningmain's default channel before the Phase 3 branding bump means the firstmainbuild at the new version is already published to the correct channel; otherwise it lands on the now-staleVS {{THIS_RELEASE_VERSION}}channel.
First, gather information (read-only queries — no PR needed):
darc get-subscriptions --exact --source-repo https://github.com/dotnet/msbuild --channel "VS {{THIS_RELEASE_VERSION}}" main branch entry.darc get-default-channels --source-repo https://github.com/dotnet/msbuild --branch vs{{THIS_RELEASE_VERSION}} Then, batch all write operations into one PR on the maestro-configuration repo.
Use --configuration-branch msbuild-{{THIS_RELEASE_VERSION}}-main-bump and --no-pr on all but the last command:
darc delete-default-channel --repo https://github.com/dotnet/msbuild --branch main --channel "VS {{THIS_RELEASE_VERSION}}" --configuration-branch msbuild-{{THIS_RELEASE_VERSION}} --no-prdarc add-default-channel --channel "VS {{NEXT_VERSION}}" --branch main --repo https://github.com/dotnet/msbuild --configuration-branch msbuild-{{THIS_RELEASE_VERSION}} --no-prdarc update-subscription --id <subscription_id_from_2.1> --channel "VS {{NEXT_VERSION}}" --configuration-branch msbuild-{{THIS_RELEASE_VERSION}} --no-prdarc add-default-channel --channel "VS {{THIS_RELEASE_VERSION}}" --branch vs{{THIS_RELEASE_VERSION}} --repo https://github.com/dotnet/msbuild --configuration-branch msbuild-{{THIS_RELEASE_VERSION}} --no-prdarc get-subscriptions --target-repo https://github.com/dotnet/msbuild --target-branch <retired_branch> darc delete-subscription --id <subscription_id> --configuration-branch msbuild-{{THIS_RELEASE_VERSION}} --no-pr--no-pr.--no-pr to open the PR on the configuration branch.Verifications (parallel — read-only, no ordering dependency):
vs{{THIS_RELEASE_VERSION}}: darc get-subscriptions --exact --target-repo https://github.com/dotnet/msbuild --source-repo https://github.com/dotnet/arcade
.NET <X> Eng channel (the channel is determined by the .NET band the branch is paired with — e.g. a branch paired with .NET 10 subscribes to .NET 10 Eng).Roslyn subscription verification intentionally omitted from the per-release checklist: there is always exactly one Roslyn subscription, targeting
mainonly and its channel does not rotate with SDK bands.NuGet subscription: when the next-to-ship SDK band rotates (e.g.
4xx→5xx), the NuGet →msbuild/mainsubscription must be re-pointed to the new band's channel.
Trigger: Phase 2 DARC updates merged (
main's default channel is nowVS {{NEXT_VERSION}}).
Create one PR in main containing all of the following changes:
eng/Versions.props: Update VersionPrefix to {{NEXT_VERSION}}.0eng/Versions.props: Update PackageValidationBaselineVersion to {{PACKAGE_VALIDATION_BASELINE_VERSION}} (see How to determine PACKAGE_VALIDATION_BASELINE_VERSION in the release skill).CompatibilitySuppressions.xml files. Run: dotnet pack MSBuild.Dev.slnf /p:ApiCompatGenerateSuppressionFile=true Trigger: 7 calendar days before
{{INSIDERS_SNAP_DATE}}.
Precondition: Phases 1–3 complete. Preview builds fromvs{{THIS_RELEASE_VERSION}}have been inserting into VSmainsince Phase 2.
Goal: Final-brand the release branch and get the final-branded bits inserted into VSmainbefore VS snaps torel/insiders.
Steps are sequential.
vs{{THIS_RELEASE_VERSION}} branch: PublicAPI.Unshipped.txt → PublicAPI.Shipped.txt for all projects with API changes. See release.md for details.scripts/Stabilize-Release.ps1 on vs{{THIS_RELEASE_VERSION}} branch: -DryRun first to preview. The script adds <DotNetFinalVersionKind>release</DotNetFinalVersionKind> on the same line as VersionPrefix (creates merge conflict for forward-flow) and changes PreReleaseVersionLabel from preview to servicing. vs{{THIS_RELEASE_VERSION}}: {{URL_OF_FINAL_BRANDING_PR}}vs{{THIS_RELEASE_VERSION}}. After the final-branding commit (4.3) merges, the official build is auto-triggered without OptProf data for the new branch and will fail. To work around:
vs{{THIS_RELEASE_VERSION}}.vs{{THIS_RELEASE_VERSION}} with the OptProf override from main — set Optional OptProfDrop Override to main's latest OptProf drop path. (Find the path in main CI logs: Windows_NT → Build → search for OptimizationData.)main before {{INSIDERS_SNAP_DATE}} (4.6 was missed) and a milestone-gate approval is now needed. If the insertion made the schedule, skip this step.vs{{THIS_RELEASE_VERSION}} into VS main (auto-generated at https://devdiv.visualstudio.com/DevDiv/_git/VS/pullrequests). The final-branded bits must be in VS main before {{INSIDERS_SNAP_DATE}} so they are included when VS snaps to rel/insiders: {{URL_OF_VS_INSERTION}} After insiders snap (only if a backport to insiders is needed):
🛑 4.7 and 4.8 are NOT part of the regular release flow — skip them entirely on a normal release.
They only apply when servicing a previously-shipped release (i.e. you actually have a hotfix commit onvs{{THIS_RELEASE_VERSION}}that needs to be inserted into VS's already-snappedrel/insidersorrel/stablebranch). If you have no such commit to service, leaveAutoInsertTargetBranchuntouched and move on to Phase 5.⚠️ When you do need to service: re-confirm which VS branch you actually want to insert into before flipping
AutoInsertTargetBranch. The default ismain, so forgetting to retarget after the snap silently lands your fix in the next VS instead of the one you're servicing.
azure-pipelines/vs-insertion.yml: retarget AutoInsertTargetBranch for vs{{THIS_RELEASE_VERSION}} from VS main → rel/insiders. This enables direct insertion of hotfix commits into the insiders branch.After stable snap (only if a backport to stable is needed):
azure-pipelines/vs-insertion.yml: retarget AutoInsertTargetBranch for vs{{THIS_RELEASE_VERSION}} → rel/stable. This enables direct insertion of hotfix commits into the stable branch.Trigger:
{{VS_SHIP_DATE}}has passed and VS release has shipped.
Steps are mostly parallel unless noted.
5.1 Push packages to nuget.org.
How publishing works: We don't push packages ourselves. We hand a link to the Shipping artifacts of the official build to the dnceng release team, and they push to nuget.org. Searching past mail for the subject _"Publish MSBuild {{THIS_RELEASE_VERSION}} to NuGet.org" for the template.
rel/stable: the Microsoft.Build component version in .corext/Configs/msbuild-components.json (e.g. 18.7.1-servicing-NNNNN-NN+<sha>). Extract just the numeric VersionPrefix from that string — drop the -servicing-NNNNN-NN+<sha> suffix — and use it as {{THIS_RELEASE_EXACT_VERSION}} (e.g. 18.7.1). Do not rely solely on the VS insertion PR — that PR targets VS main and can be superseded by a later servicing insertion before GA, whereas rel/stable reflects what actually shipped.vs{{THIS_RELEASE_VERSION}} branch and locate the build whose output version matches the one identified in 5.1a (e.g. {{THIS_RELEASE_EXACT_VERSION}}, such as 18.6.3).artifacts-shipping drop. Verify the Shipping folder contains all of:
Microsoft.Build.Utilities.Core.{{THIS_RELEASE_EXACT_VERSION}}.nupkgMicrosoft.Build.{{THIS_RELEASE_EXACT_VERSION}}.nupkgMicrosoft.Build.Framework.{{THIS_RELEASE_EXACT_VERSION}}.nupkgMicrosoft.Build.Runtime.{{THIS_RELEASE_EXACT_VERSION}}.nupkgMicrosoft.Build.Tasks.Core.{{THIS_RELEASE_EXACT_VERSION}}.nupkgMicrosoft.NET.StringTools.{{THIS_RELEASE_EXACT_VERSION}}.nupkgMicrosoft.Build.Templates.{{THIS_RELEASE_EXACT_VERSION}}.nupkgartifacts-shipping link from 5.1c and ask them to publish to nuget.org.5.2 Publish docs
How publishing works: The reference-publishing vendor team generates Microsoft Learn reference pages from the shipped MSBuild assemblies/xmldoc and then sends us a docs-repo PR with the regenerated content.
5.3 Create GitHub release:
upstream. git fetch upstream --tags && git tag --list 'v{{PREVIOUS_RELEASE_EXACT_VERSION}}' upstream is configured as the dotnet/msbuild remote. If not: git remote add upstream https://github.com/dotnet/msbuild.git.) {{THIS_RELEASE_EXACT_VERSION}}). Find the SHA in that build run's "Source version" field on the pipeline page.git checkout <commit identified in 5.3b>
git tag v{{THIS_RELEASE_EXACT_VERSION}}
git push upstream v{{THIS_RELEASE_EXACT_VERSION}}
Generate Release Notes to prepopulate. 5.4 Update BootstrapSdkVersion in eng/Versions.props if a fresh SDK was released. Check https://dotnet.microsoft.com/download/visual-studio-sdks — always verify the details for the targeted .NET version.
5.4b Update tools.dotnet in global.json to the latest released SDK in the targeted band.
5.5 Verify the overall subscription map across every still-supported branch — each vsXX.Y branch has an Arcade subscription matching its targeted .NET band, and each supported branch's outbound subscriptions land in the right downstream (e.g. SDK band, VMR).
You can find more info here.
5.6 Review this tracking issue for any process deviations. If the process changed, create a PR to update documentation/release-checklist.md with the improvements.