mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-08-29 10:09:43 +02:00
## Summary of the Pull Request Adds a `Prepare Preview Release` custom agent that autonomously turns a successful PowerToys Azure DevOps release-candidate build into a complete GitHub draft prerelease for final human review. The implementation extends the existing `release-note-generation` skill instead of duplicating it. It adds exact-build metadata resolution, published-release baseline selection, semantic PR deltas across `main` and `stable`, release asset validation, idempotent draft-only release updates, and final draft verification. ## PR Checklist - [x] **Communication:** The autonomous preview-release design was reviewed and approved before implementation - [x] **Tests:** Added/updated and all pass - [x] **Localization:** N/A; no end-user-facing strings were added - [x] **Dev docs:** Added preview scenario, delta, draft safety, and reporting references ## Detailed Description of the Pull Request / Additional comments - Adds `.github/agents/prepare-preview-release.agent.md` with a no-mid-run-decision workflow and a strict prohibition on publishing releases. - Extends `.github/skills/release-note-generation/SKILL.md` with stable/preview scenario routing while preserving the existing stable-release workflow. - Adds canonical scripts under `.github/skills/release-note-generation/scripts/` to: - Resolve and validate ADO build metadata. - Select the latest published stable or preview baseline before build queue time. - Calculate same-lineage or branch-transition PR deltas using PR numbers, cherry-pick provenance, and patch-ID equivalence. - Collect normalized PR metadata and create `release-manifest.json`. - Download and validate installers, symbols, and GPO assets, including hashes, signatures, and ZIP contents. - Create or update draft prereleases while preserving human text outside managed markers. - Verify draft flags, immutable target commit, body markers, and uploaded assets. - Updates `.pipelines/resolveBuildMetadata.ps1` and `.pipelines/v2/release.yml` with explicit `auto`, `preview-release`, and `stable-release` intent handling so preview candidates can be built from either `main` or `stable`. - Adds `.pipelines/writeReleaseMetadata.ps1` so each signed build artifact records its resolved version, channel, intent, source branch, and immutable source commit. - Keeps release publication outside the agent: the automation can only create or update a draft prerelease. ## Validation Steps Performed - `Invoke-Pester` for: - `.pipelines/tests/resolveBuildMetadata.Tests.ps1` - `.pipelines/tests/writeReleaseMetadata.Tests.ps1` - `.github/skills/release-note-generation/tests/preview-release.Tests.ps1` - 37 tests passed, covering stable-branch preview intent, metadata contracts, baseline selection, same-lineage and branch-transition deltas, patch-ID equivalence, managed-body preservation, and published-release refusal. - Parsed all added or modified PowerShell scripts with the PowerShell AST parser. - Parsed the modified pipeline YAML files with `ConvertFrom-Yaml`. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8e04a72e-3b0f-4ac4-8156-d04ea9b8bb85 Copilot-Session: e9f79ac2-9a7b-4083-834c-0d87e8c83bfd Copilot-Session: 1ecea747-b313-49a1-9969-543c01ba1be8
86 lines
2.9 KiB
PowerShell
86 lines
2.9 KiB
PowerShell
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory)][int]$DefinitionId,
|
|
[Parameter(Mandatory)][int]$BuildId,
|
|
[Parameter(Mandatory)][string]$BuildNumber,
|
|
[Parameter(Mandatory)][string]$Version,
|
|
[Parameter(Mandatory)][ValidateSet("private", "preview", "stable")][string]$Channel,
|
|
[Parameter(Mandatory)][ValidateSet("private-validation", "preview-validation", "preview-release", "stable-release")][string]$Intent,
|
|
[Parameter(Mandatory)][string]$SourceBranch,
|
|
[Parameter(Mandatory)][string]$SourceCommit,
|
|
[Parameter(Mandatory)][string]$BuildReason,
|
|
[Parameter(Mandatory)][string]$ShouldPublishPreview,
|
|
[AllowEmptyString()][string]$QueuedAt = "",
|
|
[AllowEmptyString()][string]$StartedAt = "",
|
|
[Parameter(Mandatory)][string]$OutputPath
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
if ($Version -notmatch "^\d+\.\d+\.\d+\.0$") {
|
|
throw "Version '$Version' must use the four-component PowerToys release format."
|
|
}
|
|
if ($SourceCommit -notmatch "^[0-9a-fA-F]{40}$") {
|
|
throw "SourceCommit must be a full immutable commit SHA."
|
|
}
|
|
if ($SourceBranch -notmatch "^refs/heads/.+") {
|
|
throw "SourceBranch '$SourceBranch' is not a branch ref."
|
|
}
|
|
if ($Intent -ne "private-validation" -and $SourceBranch -notin @("refs/heads/main", "refs/heads/stable")) {
|
|
throw "Intent '$Intent' is only supported from main or stable."
|
|
}
|
|
|
|
$publishPreview = switch ($ShouldPublishPreview.ToLowerInvariant()) {
|
|
"true" { $true }
|
|
"false" { $false }
|
|
default { throw "ShouldPublishPreview must be true or false." }
|
|
}
|
|
|
|
if ($publishPreview -ne ($Intent -eq "preview-release")) {
|
|
throw "ShouldPublishPreview '$publishPreview' conflicts with intent '$Intent'."
|
|
}
|
|
$expectedChannel = switch ($Intent) {
|
|
"private-validation" { "private" }
|
|
"preview-validation" { "preview" }
|
|
"preview-release" { "preview" }
|
|
"stable-release" { "stable" }
|
|
}
|
|
if ($Channel -ne $expectedChannel) {
|
|
throw "Intent '$Intent' requires channel '$expectedChannel', not '$Channel'."
|
|
}
|
|
|
|
function ConvertTo-NullablePipelineValue {
|
|
param([AllowEmptyString()][string]$Value)
|
|
|
|
if ([string]::IsNullOrWhiteSpace($Value) -or $Value.StartsWith('$(')) {
|
|
return $null
|
|
}
|
|
return $Value
|
|
}
|
|
|
|
$metadata = [ordered]@{
|
|
schemaVersion = 1
|
|
definitionId = $DefinitionId
|
|
buildId = $BuildId
|
|
buildNumber = $BuildNumber
|
|
result = "succeeded"
|
|
version = $Version
|
|
channel = $Channel
|
|
intent = $Intent
|
|
sourceBranch = $SourceBranch
|
|
sourceCommit = $SourceCommit.ToLowerInvariant()
|
|
buildReason = $BuildReason
|
|
queuedAt = ConvertTo-NullablePipelineValue -Value $QueuedAt
|
|
startedAt = ConvertTo-NullablePipelineValue -Value $StartedAt
|
|
finishedAt = $null
|
|
shouldPublishPreview = $publishPreview
|
|
}
|
|
|
|
$parent = Split-Path -Parent $OutputPath
|
|
if ($parent) {
|
|
New-Item -ItemType Directory -Path $parent -Force | Out-Null
|
|
}
|
|
$metadata | ConvertTo-Json -Depth 4 | Set-Content -LiteralPath $OutputPath -Encoding utf8
|
|
|
|
[pscustomobject]$metadata
|