From 9cde8bd7f82aa83a34d1ffcd17b576e0b78194a6 Mon Sep 17 00:00:00 2001 From: Jose Perez Rodriguez Date: Mon, 18 May 2026 20:03:37 -0700 Subject: [PATCH 1/2] Remove informational release-version build tag from release pipeline The deriveReleaseVersion step in release-publish-nuget.yml emitted `##vso[build.addbuildtag]release-version:` to surface the resolved version on the release run. The Azure Pipelines agent processes this logging command by calling AzDO REST `PUT /_apis/build/builds/{buildId}/tags/{tag}`; the `:` in `release-version:13.3.4` ends up in the URL path and the 1ES PT request-validation handler rejects it with: A potentially dangerous Request.Path value was detected from the client (:). This fails the entire step and skips the rest of the job. Confirmed in https://dev.azure.com/dnceng/internal/_build/results?buildId=2978372 (log id 53). The tag was purely informational. The functional output (`releaseVersionEffective` task variable) is still set and is what downstream stages consume. The parser that reads `release-version:` tags from the *source* build is untouched -- that tag is added by `azure-pipelines.yml` in a different agent context where the request validator is more permissive. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- eng/pipelines/release-publish-nuget.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/eng/pipelines/release-publish-nuget.yml b/eng/pipelines/release-publish-nuget.yml index 56c97eebf23..33c3ae29f4f 100644 --- a/eng/pipelines/release-publish-nuget.yml +++ b/eng/pipelines/release-publish-nuget.yml @@ -257,10 +257,6 @@ extends: Write-Host "✓ ReleaseVersionEffective: $effective" Write-Host "##vso[task.setvariable variable=releaseVersionEffective;isOutput=true]$effective" - - # Surface the resolved version on the *release* run header too, - # so it shows up in the AzDO build list / pipeline summary. - Write-Host "##vso[build.addbuildtag]release-version:$effective" name: deriveReleaseVersion displayName: 'Derive ReleaseVersion from source build tag' env: From 7c0a2326b05b4bc7d0956d51c9e5622b116711ef Mon Sep 17 00:00:00 2001 From: Jose Perez Rodriguez Date: Mon, 18 May 2026 21:44:12 -0700 Subject: [PATCH 2/2] Use ' - ' separator in release-version build tag AzDO's 1ES PT request-validation handler rejects ':' in tag names because the agent calls `PUT /_apis/build/builds/{buildId}/tags/{tag}` and ':' ends up in the URL path. This breaks both the source build (azure-pipelines.yml) and the release-publish pipeline (release-publish-nuget.yml). Failed runs: - source build: https://dev.azure.com/dnceng/internal/_build/results?buildId=2978468 - release-publish: https://dev.azure.com/dnceng/internal/_build/results?buildId=2978372 Switch the tag format from `release-version:13.3.4` to `release-version - 13.3.4`, mirroring the existing `BAR ID - ` tag style which is known to pass the validator. Update the release-publish parser (regex, messages) and docs/release-process.md accordingly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/release-process.md | 4 ++-- eng/pipelines/azure-pipelines.yml | 9 ++++++--- eng/pipelines/release-publish-nuget.yml | 21 ++++++++++++--------- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/docs/release-process.md b/docs/release-process.md index 749dba2b320..654b1d591c0 100644 --- a/docs/release-process.md +++ b/docs/release-process.md @@ -61,7 +61,7 @@ Before starting a release: pipeline regardless of branch. Pick the build that corresponds to the release branch and version you intend to ship. - Each build's tags are shown alongside its number — verify the - `release-version:X.Y.Z` tag matches the version you intend to ship + `release-version - X.Y.Z` tag matches the version you intend to ship **before** clicking Run. If the tag is missing, either re-run the source build (after the tag-emitting change in `azure-pipelines.yml` is on that release branch) or pass an explicit `ReleaseVersion` @@ -75,7 +75,7 @@ Before starting a release: | Parameter | Description | Example | |-----------|-------------|---------| - | `ReleaseVersion` | Override for the version label (used as `v` tag). **Leave as `auto` to derive from the source build's `release-version:*` tag** — the normal case. Only set this when re-shipping under a corrected tag. | `auto` | + | `ReleaseVersion` | Override for the version label (used as `v` tag). **Leave as `auto` to derive from the source build's `release-version - *` tag** — the normal case. Only set this when re-shipping under a corrected tag. | `auto` | | `IsPrerelease` | `true` for preview releases | `false` | | `DryRun` | Set `true` to test without publishing or tagging | `false` | | `GaChannelName` | Target GA channel | `Aspire 9.x GA` | diff --git a/eng/pipelines/azure-pipelines.yml b/eng/pipelines/azure-pipelines.yml index 99311646287..691bf80f91a 100644 --- a/eng/pipelines/azure-pipelines.yml +++ b/eng/pipelines/azure-pipelines.yml @@ -355,11 +355,14 @@ extends: Write-Host "##vso[task.setvariable variable=aspireVersion;isOutput=true]$version" # Tag the build so the release pipeline can auto-derive ReleaseVersion - # without operators retyping it. The 'release-version:' prefix avoids + # without operators retyping it. The 'release-version - ' prefix avoids # collisions with other custom build tags (e.g., 1ES compliance tags), # and the tag is visible in the AzDO source-build picker so operators - # can verify the version at queue time. - Write-Host "##vso[build.addbuildtag]release-version:$version" + # can verify the version at queue time. The format intentionally mirrors + # the ' - '-separated 'BAR ID - ' tag style: AzDO's 1ES PT request + # validator rejects ':' in tag names because the tag becomes part of the + # URL path of the addbuildtag REST call. + Write-Host "##vso[build.addbuildtag]release-version - $version" name: computeVersion displayName: 🟣Compute Aspire version from nupkg diff --git a/eng/pipelines/release-publish-nuget.yml b/eng/pipelines/release-publish-nuget.yml index 33c3ae29f4f..fe7d6e589f2 100644 --- a/eng/pipelines/release-publish-nuget.yml +++ b/eng/pipelines/release-publish-nuget.yml @@ -172,7 +172,7 @@ extends: Write-Host "===============================" displayName: 'Log Stage Info' - # Auto-derive ReleaseVersion from the source build's `release-version:*` + # Auto-derive ReleaseVersion from the source build's `release-version - *` # tag (set by eng/pipelines/azure-pipelines.yml after the version is # computed from the Aspire.Hosting.AppHost nupkg). This means operators # don't have to retype the version when queuing a release — the tag @@ -185,7 +185,7 @@ extends: # escape hatch for re-shipping under a corrected tag. # # Failure semantics: hard-fail when no override is supplied AND the - # source build has no `release-version:*` tag (e.g., a pre-tagging + # source build has no `release-version - *` tag (e.g., a pre-tagging # build was selected). The fix is either to backport the tag-add # change to the relevant release branch and re-run the source build, # or pass an explicit -ReleaseVersion override. @@ -218,26 +218,29 @@ extends: Write-Host "Build tags: $($response.value -join ', ')" - # Tags look like 'release-version:13.3.3'. Filter and parse. + # Tags look like 'release-version - 13.3.3'. Filter and parse. + # The ' - ' separator (rather than ':') matches the 'BAR ID - ' + # tag style and avoids AzDO's 1ES PT request validator rejecting ':' + # in tag names (it becomes part of the addbuildtag REST URL path). # NB: variable named $tagMatches (not $matches) to avoid shadowing # PowerShell's automatic $Matches hashtable. - $tagMatches = @($response.value | Where-Object { $_ -match '^release-version:(.+)$' }) + $tagMatches = @($response.value | Where-Object { $_ -match '^release-version - (.+)$' }) $derived = $null if ($tagMatches.Count -eq 0) { if ([string]::IsNullOrWhiteSpace($override)) { - $msg = "Source build $buildId has no 'release-version:*' tag and no -ReleaseVersion was supplied. " + + $msg = "Source build $buildId has no 'release-version - *' tag and no -ReleaseVersion was supplied. " + "Either re-queue with an explicit -ReleaseVersion override, or run a newer source " + "build that includes the tag-emitting step from eng/pipelines/azure-pipelines.yml." Write-Error $msg exit 1 } - Write-Host "##vso[task.logissue type=warning]No 'release-version:*' tag on source build $buildId; using operator-supplied -ReleaseVersion '$override'." + Write-Host "##vso[task.logissue type=warning]No 'release-version - *' tag on source build $buildId; using operator-supplied -ReleaseVersion '$override'." } elseif ($tagMatches.Count -gt 1) { - Write-Error "Source build $buildId has multiple 'release-version:*' tags ($($tagMatches -join ', ')). Refusing to guess; pass -ReleaseVersion explicitly." + Write-Error "Source build $buildId has multiple 'release-version - *' tags ($($tagMatches -join ', ')). Refusing to guess; pass -ReleaseVersion explicitly." exit 1 } else { - $derived = ($tagMatches[0] -replace '^release-version:', '').Trim() + $derived = ($tagMatches[0] -replace '^release-version - ', '').Trim() if ($derived -notmatch '^\d+\.\d+\.\d+(-[A-Za-z0-9.-]+)?$') { Write-Error "Derived release version '$derived' does not look like valid semver (X.Y.Z[-suffix])." exit 1 @@ -248,7 +251,7 @@ extends: # Resolve the effective value: operator override wins, derived is fallback. if (-not [string]::IsNullOrWhiteSpace($override)) { if ($derived -and $override -ne $derived) { - Write-Host "##vso[task.logissue type=warning]Operator-supplied -ReleaseVersion '$override' does NOT match source-build tag 'release-version:$derived'. Proceeding with operator override." + Write-Host "##vso[task.logissue type=warning]Operator-supplied -ReleaseVersion '$override' does NOT match source-build tag 'release-version - $derived'. Proceeding with operator override." } $effective = $override.Trim() } else {