diff --git a/documentation/release-checklist.md b/documentation/release-checklist.md index 15d01fcc44f..55af1502033 100644 --- a/documentation/release-checklist.md +++ b/documentation/release-checklist.md @@ -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 `release` 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 `release` 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}} diff --git a/documentation/release.md b/documentation/release.md index febe2cc1317..c00984db6d3 100644 --- a/documentation/release.md +++ b/documentation/release.md @@ -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 `release` (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 diff --git a/scripts/Stabilize-Release.ps1 b/scripts/Stabilize-Release.ps1 new file mode 100644 index 00000000000..3053ff2ca09 --- /dev/null +++ b/scripts/Stabilize-Release.ps1 @@ -0,0 +1,101 @@ +<# +.SYNOPSIS + Performs the stabilization portion of the MSBuild release process. + +.DESCRIPTION + This script modifies eng/Versions.props to: + 1. Add release 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' + +# 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: X.Y.Z +# Replace with: X.Y.Zrelease +$versionPrefixPattern = '([^<]+)(\s*\r?\n)' + +if ($content -match 'release') { + Write-Error "DotNetFinalVersionKind is already set to 'release'. Has stabilization already been done?" + exit 1 +} elseif ($content -match $versionPrefixPattern) { + $newVersionPrefixLine = '$1release$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: X.Y.Z" + exit 1 +} + +# Pattern 2: Change PreReleaseVersionLabel from 'preview' to 'servicing' +# Match: preview +# Replace with: servicing +$preReleasePattern = 'preview' + +if ($content -match 'servicing') { + Write-Error "PreReleaseVersionLabel is already 'servicing'. Has stabilization already been done?" + exit 1 +} elseif ($content -match $preReleasePattern) { + $content = $content -replace $preReleasePattern, 'servicing' + 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 '(\d+\.\d+)\.\d+') { + $versionForCommit = $Matches[1] +} 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)" +}