Fix 13.4 staging CLI dropping nuget.config without Aspire package source mapping - #17528
Conversation
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 17528Or
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 17528" |
There was a problem hiding this comment.
Pull request overview
This PR fixes channel computation in the AzDO native-sign build template so release-branch (“release/” and “internal/release/”) builds bake AspireCliChannel=staging even when StabilizePackageVersion=true causes DotNetFinalVersionKind=release. This aligns the baked CLI identity channel with how release-branch artifacts are published (staging feed first), preventing aspire init from generating a nuget.config that omits the staging-feed mapping for Aspire.*.
Changes:
- Reorders the channel-selection conditionals so the release-branch regex check runs before the
versionKind == 'release'check. - Adds an explanatory comment (with issue link) documenting why the ordering matters for stabilized release-branch builds.
Show a summary per file
| File | Description |
|---|---|
| eng/pipelines/templates/build_sign_native.yml | Fixes AspireCliChannel computation ordering so release-branch builds correctly bake staging instead of stable. |
Copilot's findings
- Files reviewed: 1/1 changed files
- Comments generated: 0
…bilizing The channel-compute step in build_sign_native.yml was checking $versionKind -eq 'release' BEFORE the release-branch regex check. A 13.4 staging build runs from a release/* branch with StabilizePackageVersion=true, which sets DotNetFinalVersionKind=release, so the wrong arm fired and baked AspireCliChannel=stable into the binary. Downstream, aspire init reads CliExecutionContext.IdentityChannel to pick the channel mappings it writes into the workspace nuget.config. With identity=stable there's no Aspire.* → staging-feed mapping, so aspire add tries to resolve packages from nuget.org and either gets 13.3.5 or fails outright (the apphost.cs template pins #:sdk Aspire.AppHost.Sdk@13.4.0+<sha>, which isn't on nuget.org). Swap the conditions so the release-branch check runs first. Release- branch builds are always staging artifacts; only release-shaped non-release-branch builds (effectively none in practice) get stable. Fixes #17527 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
30ca039 to
b869bf3
Compare
With release-branch builds now defaulting to 'staging' (so stabilizing dogfood builds aren't mis-baked as 'stable'), there was no remaining path to produce a real 'stable' GA ship binary — the same release/* branch that produces the staging dogfood drops also produces the final ship build, and the pipeline has no other signal to tell them apart. Add a runtime pipeline parameter 'aspireCliChannelOverride' (auto | stable | staging | daily, default auto) to azure-pipelines.yml and thread it through every build_sign_native.yml invocation. When the release manager kicks off the official GA ship build, they set this to 'stable' so the distributed binary bakes AspireCliChannel=stable and aspire init writes the nuget.org-only nuget.config that matches the promoted package set. Routine stabilizing builds leave it on 'auto' and continue to bake 'staging'. The override is validated against the same accepted-channel set that IdentityChannelReader.IsValidChannel enforces at CLI startup so a typo fails the pipeline step rather than producing a binary that refuses to boot. pr-<N> is intentionally excluded from the override set since PR builds always come from the PullRequest reason arm. The unofficial pipeline doesn't get the parameter — its test builds should always derive the channel from branch+reason, and the template's default 'auto' achieves that. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
radical
left a comment
There was a problem hiding this comment.
Posted 1 release correctness issue.
| - name: aspireCliChannelOverride | ||
| displayName: 'Aspire CLI channel override (auto = derive from branch; set to stable for the GA ship build)' | ||
| type: string | ||
| default: 'auto' |
There was a problem hiding this comment.
Because auto now bakes staging for every release/* build, the GA ship path depends on someone remembering to queue the source microsoft-aspire build with this override set to stable. The release docs currently only tell release managers to select a successful signed source build in release-publish-nuget, so following those instructions can publish GA aspire-cli-* assets whose binary still identifies as staging and writes staging-feed config from aspire init.
Please update the release/source-build docs to call out aspireCliChannelOverride=stable for the final GA source build (leaving auto for staging dogfood builds), and add a release-pipeline guard that verifies the selected source build's CLI archive/tool package has AspireCliChannel=stable before publishing it.
There was a problem hiding this comment.
+1 — I share the same concern. Needing to remember a queue-time parameter on the GA ship build is not where we want to be long-term; it's the kind of thing that gets missed exactly once and then we ship a binary that says staging to nuget.org users.
This is fine for 13.4 since we're days from ship and the override at least makes the choice explicit, but I've logged #17550 to decouple the channel identity from build-time baking entirely (sidecar manifest written by the acquisition script) so a validated staging artifact can be promoted to stable without a re-spin. Targeted at 13.5. Would love your input there.
The staging-channel synthesis in PackagingService defaulted to
PackageChannelQuality.Both for staging-identity CLIs. With Both,
useSharedFeed=true and Aspire.* gets routed to the shared dnceng/dotnet9
daily feed -- which only contains prerelease-tagged 13.4.0-preview.*
packages, not the stable-shaped 13.4.0 packages produced during release
stabilization (StabilizePackageVersion=true).
Net effect on the just-shipped staging build of 13.4: `aspire init`
drops a NuGet.config pointing Aspire.* at dotnet9, then `aspire add yarp`
fails to resolve Aspire.Hosting.Yarp 13.4.0 because dotnet9 doesn't
carry it. The packages actually live in the SHA-derived
darc-pub-microsoft-aspire-<hash> feed.
Fix: when the CLI's identity is staging, derive the synthesized
channel's default quality from the CLI build's version shape:
- Stable-shaped (no semver prerelease tag) -> Stable, which makes
useSharedFeed=false and routes Aspire.* to the SHA-derived darc
feed where stabilizing packages actually live.
- Prerelease-shaped -> Both (the historical default), since SHA-
specific darc feeds are only created for stable release-branch
builds and prerelease staging CLIs must use the shared feed.
The identity-staging branch runs before the requested/configured
branches in the if/else because `init` (and many other commands) calls
GetChannelsAsync(requestedChannelName: "staging") when the running
CLI's identity is staging -- short-circuiting on the requested branch
would re-introduce the bug.
The version-shape predicate is injected via constructor so unit tests
can deterministically exercise both paths regardless of the test-host
assembly's baked InformationalVersion.
Validated end-to-end with a locally-built NAOT `aspire` binary
(`/p:AspireCliChannel=staging`) + overrideStagingFeed pointing at
darc-pub-microsoft-aspire-0f514452: `aspire init` -> `aspire add yarp`
now resolves Aspire.Hosting.Yarp 13.4.0 instead of falling back to
13.3.5.
Refs #17527
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The stabilization-check CI job builds the test host with StabilizePackageVersion=true, which bakes a stable-shaped (no '-') informational version into Aspire.Cli. PackagingService's new identity-staging branch then defaults quality to Stable and requires a SHA suffix in the assembly's InformationalVersion to compute the darc-pub feed URL. Stabilized test-host assemblies don't carry a +sha suffix, so CreateStagingChannel returned null and the UpdateCommand_WhenStagingIdentityRegistersChannel_UsesStagingForUnpinnedProject test fell back to the default channel instead of staging. Default CliTestHelper.PackagingServiceFactory to inject isStableShapedCliVersion: () => false so command-level tests get deterministic prerelease-shaped behavior (quality=Both → shared dotnet9 feed) regardless of how the test host was built. Tests that specifically exercise the stable-shape branch (in PackagingServiceTests) construct PackagingService directly and already pass an explicit predicate. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
❓ CLI E2E Tests unknown — 107 passed, 0 failed, 2 unknown (commit View all recordings
📹 Recordings uploaded automatically from CI run #26506998459 |
radical
left a comment
There was a problem hiding this comment.
Two test-coverage gaps. The fixes themselves look correct — cascade reorder, override parameter, and version-shape predicate all verified end-to-end. These comments flag gaps in the test surface only, not bugs in the changes.
| .Build(); | ||
| var packagingService = new PackagingService(executionContext, new FakeNuGetPackageCache(), new TestFeatures(), configuration, NullLogger<PackagingService>.Instance, isStableShapedCliVersion: () => true); | ||
|
|
||
| var channels = await packagingService.GetChannelsAsync().DefaultTimeout(); |
There was a problem hiding this comment.
The PR's core change is putting stagingIdentityChannel first in the if/else in PackagingService.GetChannelsAsync so the stagingChannelRequested branch doesn't short-circuit on a staging-baked CLI. This test sets identityChannel: Staging but calls GetChannelsAsync() with no requestedChannelName, so it doesn't exercise the combination that actually arises in real flows. A future refactor that reordered the if/else (stagingChannelRequested first → Both) would still pass this test, and the #17527 misroute would silently regress.
Concretely, both flags are true simultaneously in:
InitCommand.cs:671—GetChannelsAsync(ct, identityChannel)on a staging CLI runningaspire initIntegrationPackageSearchService.cs:26(used byaspire add) andDotNetBasedAppHostServerProject.cs:334, when the project haschannel: staging
Suggest adding two variants that pass requestedChannelName: PackageChannelNames.Staging:
- identity=staging + requested=staging + stable-shaped →
Stable(locks in the conditional ordering) - identity=staging + requested=staging + prerelease-shaped →
Both(inverse; asserts the predicate is consulted on the identity path)
A _AndConfigurationChannelIsAlsoStaging variant (configuration["channel"] = "staging") would similarly lock the stagingChannelConfigured arm.
| $channel = "pr-$prNumber" | ||
| } elseif ($versionKind -eq 'release') { | ||
| $channel = 'stable' | ||
| } elseif ($sourceBranch -match '^refs/heads/(release|internal/release)/') { |
There was a problem hiding this comment.
The cascade reorder is the headline pipeline fix and currently has no test coverage. The repo already has the infrastructure: tests/Infrastructure.Tests/PowerShellScripts/PowerShellCommand.cs invokes standalone .ps1 files via pwsh (used by StageNativeCliToolPackagesTests, BuildTestMatrixTests, etc.).
To make this cascade testable, extract this inline pwsh into eng/scripts/compute-cli-channel.ps1:
param(
[Parameter(Mandatory=$true)][string]$Reason,
[Parameter(Mandatory=$true)][string]$SourceBranch,
[string]$PrNumber = '',
[string]$Override = 'auto',
[Parameter(Mandatory=$true)][string]$VersionKind
)
# ...cascade...
Write-Output $channelThe YAML step becomes a single pwsh -File call. A [Theory]-based ComputeCliChannelTests then covers cases with zero coverage today:
- release-branch + versionKind=release →
staging(the bug fix) - release-branch + versionKind != release →
staging(early stabilization phase) - main + versionKind=release →
stable(unchanged) - main + versionKind != release →
daily(unchanged) - override=
stableon a release branch →stable(the ship-build path) - override=
auto→ falls through - PR build with non-digit
$prNumber→ throw (defense-in-depth that's untested today) - override=
garbage→ throw (the inner-notinvalidation; only fires if a non-pipeline caller bypasses the queue-timevalues:constraint)
|
I'm going to merge this in to unblock dogfooding of staging. ANy other feedback can be addressed as a follow up. |
PR Testing ReportPR Information
CLI Version Verification
Changes AnalyzedFiles Changed
Change Categories
Scope NoteThe exact stable-shaped Test Scenarios ExecutedScenario 1: Dogfood PR template creationObjective: Create a fresh Aspire app from the PR package hive and verify generated NuGet package-source mapping. Steps:
Evidence:
Observations: Scenario 2: AppHost startup smoke testObjective: Verify a freshly generated starter AppHost can start under the PR CLI and report resources. Steps:
Evidence:
Observations: Scenario 3: Existing output directory boundaryObjective: Verify aspire new does not overwrite an existing generated project directory. Steps:
Evidence:
Observations: Expected Unhappy-Path Outcome: Non-zero exit code with a clear validation error; existing files remain untouched. Scenario 4: Invalid AppHost path boundaryObjective: Verify follow-up AppHost commands fail safely for a missing --apphost path. Steps:
Evidence:
Observations: Expected Unhappy-Path Outcome: Non-zero exit code with a clear missing AppHost path error. Summary
Overall Result[PASS] PR VERIFIED Recommendations
|
|
✅ No documentation update needed. docs_required → already documented by name Triggered signals (1): Evidence: The PR body's validation section contains
The PR is a bug fix that corrects internal staging channel routing in |
…rce mapping (#17528) * Bake AspireCliChannel=staging for release-branch builds even when stabilizing The channel-compute step in build_sign_native.yml was checking $versionKind -eq 'release' BEFORE the release-branch regex check. A 13.4 staging build runs from a release/* branch with StabilizePackageVersion=true, which sets DotNetFinalVersionKind=release, so the wrong arm fired and baked AspireCliChannel=stable into the binary. Downstream, aspire init reads CliExecutionContext.IdentityChannel to pick the channel mappings it writes into the workspace nuget.config. With identity=stable there's no Aspire.* → staging-feed mapping, so aspire add tries to resolve packages from nuget.org and either gets 13.3.5 or fails outright (the apphost.cs template pins #:sdk Aspire.AppHost.Sdk@13.4.0+<sha>, which isn't on nuget.org). Swap the conditions so the release-branch check runs first. Release- branch builds are always staging artifacts; only release-shaped non-release-branch builds (effectively none in practice) get stable. Fixes #17527 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add aspireCliChannelOverride pipeline parameter for GA ship builds With release-branch builds now defaulting to 'staging' (so stabilizing dogfood builds aren't mis-baked as 'stable'), there was no remaining path to produce a real 'stable' GA ship binary — the same release/* branch that produces the staging dogfood drops also produces the final ship build, and the pipeline has no other signal to tell them apart. Add a runtime pipeline parameter 'aspireCliChannelOverride' (auto | stable | staging | daily, default auto) to azure-pipelines.yml and thread it through every build_sign_native.yml invocation. When the release manager kicks off the official GA ship build, they set this to 'stable' so the distributed binary bakes AspireCliChannel=stable and aspire init writes the nuget.org-only nuget.config that matches the promoted package set. Routine stabilizing builds leave it on 'auto' and continue to bake 'staging'. The override is validated against the same accepted-channel set that IdentityChannelReader.IsValidChannel enforces at CLI startup so a typo fails the pipeline step rather than producing a binary that refuses to boot. pr-<N> is intentionally excluded from the override set since PR builds always come from the PullRequest reason arm. The unofficial pipeline doesn't get the parameter — its test builds should always derive the channel from branch+reason, and the template's default 'auto' achieves that. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Route stabilizing staging CLI to SHA-derived darc feed The staging-channel synthesis in PackagingService defaulted to PackageChannelQuality.Both for staging-identity CLIs. With Both, useSharedFeed=true and Aspire.* gets routed to the shared dnceng/dotnet9 daily feed -- which only contains prerelease-tagged 13.4.0-preview.* packages, not the stable-shaped 13.4.0 packages produced during release stabilization (StabilizePackageVersion=true). Net effect on the just-shipped staging build of 13.4: `aspire init` drops a NuGet.config pointing Aspire.* at dotnet9, then `aspire add yarp` fails to resolve Aspire.Hosting.Yarp 13.4.0 because dotnet9 doesn't carry it. The packages actually live in the SHA-derived darc-pub-microsoft-aspire-<hash> feed. Fix: when the CLI's identity is staging, derive the synthesized channel's default quality from the CLI build's version shape: - Stable-shaped (no semver prerelease tag) -> Stable, which makes useSharedFeed=false and routes Aspire.* to the SHA-derived darc feed where stabilizing packages actually live. - Prerelease-shaped -> Both (the historical default), since SHA- specific darc feeds are only created for stable release-branch builds and prerelease staging CLIs must use the shared feed. The identity-staging branch runs before the requested/configured branches in the if/else because `init` (and many other commands) calls GetChannelsAsync(requestedChannelName: "staging") when the running CLI's identity is staging -- short-circuiting on the requested branch would re-introduce the bug. The version-shape predicate is injected via constructor so unit tests can deterministically exercise both paths regardless of the test-host assembly's baked InformationalVersion. Validated end-to-end with a locally-built NAOT `aspire` binary (`/p:AspireCliChannel=staging`) + overrideStagingFeed pointing at darc-pub-microsoft-aspire-0f514452: `aspire init` -> `aspire add yarp` now resolves Aspire.Hosting.Yarp 13.4.0 instead of falling back to 13.3.5. Refs #17527 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Pin PackagingService stable-shape predicate to false in CliTestHelper The stabilization-check CI job builds the test host with StabilizePackageVersion=true, which bakes a stable-shaped (no '-') informational version into Aspire.Cli. PackagingService's new identity-staging branch then defaults quality to Stable and requires a SHA suffix in the assembly's InformationalVersion to compute the darc-pub feed URL. Stabilized test-host assemblies don't carry a +sha suffix, so CreateStagingChannel returned null and the UpdateCommand_WhenStagingIdentityRegistersChannel_UsesStagingForUnpinnedProject test fell back to the default channel instead of staging. Default CliTestHelper.PackagingServiceFactory to inject isStableShapedCliVersion: () => false so command-level tests get deterministic prerelease-shaped behavior (quality=Both → shared dotnet9 feed) regardless of how the test host was built. Tests that specifically exercise the stable-shape branch (in PackagingServiceTests) construct PackagingService directly and already pass an explicit predicate. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update eng/pipelines/templates/build_sign_native.yml --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Jose Perez Rodriguez <joperezr@microsoft.com>
…7646) The signed-build verifier ran 'aspire new aspire-starter' after extracting the CLI archive, claiming to exercise 'template engine + bundle self-extraction without requiring a NuGet restore'. That claim was wrong: 'aspire new' resolves template versions via TemplateNuGetConfigService.ResolveTemplatePackageAsync, which always queries a NuGet feed. The step only ever succeeded on release branches because builds were mis-baked with AspireCliChannel=stable, which routed the implicit identity-channel lookup to nuget.org and found a previously-shipped Aspire.ProjectTemplates version. Once #17528 corrected the release-branch builds to bake AspireCliChannel=staging, the identity channel switched to a staging feed that is not reachable from the 1ES signed-build agent, and the step started failing with 'No template versions were found' in the internal build pipeline. This is the same egress reason the TypeScript starter check was already removed for (#17274, tracked in #17345). Re-adding meaningful starter coverage in the signed-build verifier requires either an internal NuGet mirror or a no-network template path. Drop the step (and the corresponding test assertion); the verifier still covers archive layout, sidecar contract, and 'aspire --version' execution. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fixes #17527.
The 13.4 staging CLI ships claiming
channel: stable, which makesaspire initdrop anuget.configthat points only at nuget.org — soaspire add yarpresolves the prior stable (13.3.5), or fails to find the stabilizingAspire.AppHost.Sdk@13.4.0+0f5144527….Three coordinated fixes
1.
eng/pipelines/templates/build_sign_native.yml— reorder the channel-detect cascadeThe auto-detect previously checked
versionKind -eq 'release'before the release-branch regex.StabilizePackageVersion=true(every release-branch stabilizing build) setsDotNetFinalVersionKind=release, so the staging build was baked asAspireCliChannel=stable. Reordering so the release-branch regex runs first makes stabilizing release-branch builds resolve tostagingcorrectly.2.
eng/pipelines/azure-pipelines.yml—aspireCliChannelOverrideruntime parameterReordering (1) means release-branch builds no longer auto-resolve to
stable. The actual GA ship build still needs to bakestable, so a runtime parameter (auto|stable|staging|daily, defaultauto) lets the operator pick the channel explicitly at queue time.autofalls through to the auto-detect cascade.3.
src/Aspire.Cli/Packaging/PackagingService.cs— version-shape-driven default qualityEven with fixes (1) + (2), the staging-channel synthesis was routing Aspire.* to the shared dnceng/dotnet9 daily feed instead of the SHA-derived
darc-pub-microsoft-aspire-<hash>feed where stabilizing packages actually live. Root cause:stagingIdentityChanneldefaulted quality toBoth, makinguseSharedFeed=true. dotnet9 only carries13.4.0-preview.1.*, not stable-shaped13.4.0.Fix: when the CLI's identity is
staging, derive the default quality from the CLI build's version shape:Stable→ SHA-derived darc feed (works for stabilizing builds).Both→ shared dotnet9 (historical behavior; SHA feeds only exist for stable builds).The identity branch runs before the requested/configured branches in the if/else because
initcallsGetChannelsAsync(requestedChannelName: "staging")when identity is staging — short-circuiting there would re-introduce the bug. The version-shape predicate is injected via constructor for deterministic unit-test coverage.Validation
End-to-end validated with a locally-built NAOT
aspirebinary:Resolved staging channel:
feed=…darc-pub-microsoft-aspire-0f514452…, quality=Stable, pinnedVersion=(none).PackagingServiceTests(50/50) pass.Microsoft Reviewers: Open in CodeFlow