Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion documentation/release-checklist.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ if it is not, `darc add-default-channel --channel "VS {{THIS_RELEASE_VERSION}}"

### Final branding
- [ ] Prepare final branding PR for `vs{{THIS_RELEASE_VERSION}}`: {{URL_OF_FINAL_BRANDING_PR}} \
Edit Version.props file - add `<DotNetFinalVersionKind>release</DotNetFinalVersionKind>` as a suffix (on same line! - to intentionaly make it merge conflict on flows to main) after the `VersionPrefix` \
Run `scripts/Stabilize-Release.ps1` to update `eng/Versions.props` (adds `<DotNetFinalVersionKind>release</DotNetFinalVersionKind>` on same line as `VersionPrefix` and changes `PreReleaseVersionLabel` to `servicing`). Use `-DryRun` to preview changes. \
e.g.: #11130, #10697
- [ ] Merge final branding to `vs{{THIS_RELEASE_VERSION}}` branch
- [ ] Update perfstar MSBuild insertions configuration: [example PR](https://dev.azure.com/devdiv/DevDiv/_git/dotnet-perfstar/pullrequest/522843): {{URL_OF_PERFSTAR_PR}}
Expand Down
2 changes: 2 additions & 0 deletions documentation/release.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ This is a description of the steps required to release MSBuild. It is **incomple

To produce packages without a `-prerelease` suffix, we need to specify `<DotNetFinalVersionKind>release</DotNetFinalVersionKind>` (see the [Arcade versioning docs]). This is ideally done on the same line as the version specification so that it causes a Git merge conflict when merging to the next release's branch. See [#6902](https://github.com/dotnet/msbuild/pull/6902) for an example.

Run `scripts/Stabilize-Release.ps1` to automate this process. The script modifies `eng/Versions.props` to add `DotNetFinalVersionKind` and change `PreReleaseVersionLabel` from `preview` to `servicing`. Use `-DryRun` to preview changes before applying them.

[Arcade versioning docs]: https://github.com/dotnet/arcade/blob/31cecde14e1512ecf60d2d8afb71fd240919f4a8/Documentation/CorePackages/Versioning.md

## Public API
Expand Down
101 changes: 101 additions & 0 deletions scripts/Stabilize-Release.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<#
.SYNOPSIS
Performs the stabilization portion of the MSBuild release process.

.DESCRIPTION
This script modifies eng/Versions.props to:
1. Add <DotNetFinalVersionKind>release</DotNetFinalVersionKind> on the same line as VersionPrefix
(to create a merge conflict in forward-flow, as per release documentation)
2. Change PreReleaseVersionLabel from 'preview' to 'servicing'

See documentation/release.md and documentation/release-checklist.md for details.

.PARAMETER DryRun
If specified, shows what changes would be made without actually modifying the file.

.EXAMPLE
.\Stabilize-Release.ps1
Performs the stabilization changes to eng/Versions.props.

.EXAMPLE
.\Stabilize-Release.ps1 -DryRun
Shows what changes would be made without modifying any files.
#>

[CmdletBinding()]
param(
[switch]$DryRun
)

Set-StrictMode -Version 'Latest'
$ErrorActionPreference = 'Stop'
Comment thread
rainersigwald marked this conversation as resolved.

# Find repo root by looking for eng/Versions.props
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$repoRoot = Split-Path -Parent $scriptDir

$versionsPropsPath = Join-Path $repoRoot 'eng\Versions.props'

if (-not (Test-Path $versionsPropsPath)) {
Write-Error "Could not find eng/Versions.props at expected location: '$versionsPropsPath'. Repository structure may have changed."
exit 1
}

Write-Host "Processing: $versionsPropsPath" -ForegroundColor Cyan

$content = Get-Content $versionsPropsPath -Raw

# Pattern 1: Add DotNetFinalVersionKind on the same line as VersionPrefix
# Match: <VersionPrefix>X.Y.Z</VersionPrefix>
# Replace with: <VersionPrefix>X.Y.Z</VersionPrefix><DotNetFinalVersionKind>release</DotNetFinalVersionKind><!-- Keep next to VersionPrefix to create a conflict in forward-flow -->
$versionPrefixPattern = '(<VersionPrefix>[^<]+</VersionPrefix>)(\s*\r?\n)'

if ($content -match '<DotNetFinalVersionKind>release</DotNetFinalVersionKind>') {
Write-Error "DotNetFinalVersionKind is already set to 'release'. Has stabilization already been done?"
exit 1
} elseif ($content -match $versionPrefixPattern) {
$newVersionPrefixLine = '$1<DotNetFinalVersionKind>release</DotNetFinalVersionKind><!-- Keep next to VersionPrefix to create a conflict in forward-flow -->$2'
$content = $content -replace $versionPrefixPattern, $newVersionPrefixLine
Write-Host " Added DotNetFinalVersionKind=release on VersionPrefix line." -ForegroundColor Green
} else {
Write-Error "Could not find VersionPrefix element in expected format. Expected pattern like: <VersionPrefix>X.Y.Z</VersionPrefix>"
exit 1
}

# Pattern 2: Change PreReleaseVersionLabel from 'preview' to 'servicing'
# Match: <PreReleaseVersionLabel>preview</PreReleaseVersionLabel>
# Replace with: <PreReleaseVersionLabel>servicing</PreReleaseVersionLabel>
$preReleasePattern = '<PreReleaseVersionLabel>preview</PreReleaseVersionLabel>'

if ($content -match '<PreReleaseVersionLabel>servicing</PreReleaseVersionLabel>') {
Write-Error "PreReleaseVersionLabel is already 'servicing'. Has stabilization already been done?"
exit 1
} elseif ($content -match $preReleasePattern) {
$content = $content -replace $preReleasePattern, '<PreReleaseVersionLabel>servicing</PreReleaseVersionLabel>'
Write-Host " Changed PreReleaseVersionLabel from 'preview' to 'servicing'." -ForegroundColor Green
} else {
Write-Error "Could not find PreReleaseVersionLabel with value 'preview'. Current value may not be 'preview'."
exit 1
}

# Extract version for commit message (e.g., "18.4" from "18.4.0")
$versionForCommit = ""
if ($content -match '<VersionPrefix>(\d+\.\d+)\.\d+</VersionPrefix>') {
$versionForCommit = $Matches[1]
Comment thread
rainersigwald marked this conversation as resolved.
} else {
Write-Error "Could not extract VersionPrefix for commit message."
exit 1
}

if ($DryRun) {
Write-Host "`n=== DRY RUN - No changes written ===" -ForegroundColor Magenta
Write-Host "`nResulting content of eng/Versions.props (first 30 lines):" -ForegroundColor Cyan
$content -split "`n" | Select-Object -First 30 | ForEach-Object { Write-Host $_ }
} else {
[System.IO.File]::WriteAllText($versionsPropsPath, $content, [System.Text.Encoding]::UTF8)
Write-Host "`nStabilization complete. Changes written to: $versionsPropsPath" -ForegroundColor Green
Write-Host "`nNext steps:" -ForegroundColor Cyan
Write-Host " 1. Review the changes: git diff eng/Versions.props"
Write-Host " 2. Commit: git commit -am 'Stable branding for $versionForCommit release'"
Write-Host " 3. Create PR to the release branch (e.g., vs$versionForCommit)"
}
Loading