Skip to content

Auto-register staging channel for staging CLI#17155

Merged
radical merged 9 commits into
microsoft:mainfrom
radical:radical/issue-17121-staging-channel
May 18, 2026
Merged

Auto-register staging channel for staging CLI#17155
radical merged 9 commits into
microsoft:mainfrom
radical:radical/issue-17121-staging-channel

Conversation

@radical
Copy link
Copy Markdown
Member

@radical radical commented May 16, 2026

Description

Fixes #17121

A CLI binary built with the staging identity did not automatically register the staging package channel. That meant channel-aware commands such as aspire new could fall back to the implicit/default package source unless users also configured channel: staging or enabled the staging feature flag.

This change registers the staging package channel when the running CLI identity is staging, when project configuration requests channel: staging, or when a command explicitly requests the staging channel. Staging channel materialization now uses prerelease-capable package quality by default for identity/config/requested staging so staging builds can resolve staging packages correctly.

The change also preserves explicit channel precedence and makes mixed CLI routes safer:

  • staging-created projects can persist channel: staging
  • project-local staging pins are honored even when --apphost points outside the launch directory
  • add/search/update/restore/new paths resolve staging through the shared packaging service
  • explicit walk-back with aspire update --channel stable persists the stable channel for guest projects even when package versions are already current
  • aspire config set -g overrideStagingFeed <url> is honored by later CLI invocations and flows into generated NuGet.config for staging C# template creation

User-facing usage

No new command syntax is required. Existing commands now honor the staging CLI identity automatically:

aspire new

When run from a staging CLI build, channel-pinning templates resolve from the registered staging channel and persist:

{
  "channel": "staging"
}

Users can still override the staging package feed globally:

aspire config set -g overrideStagingFeed https://example.com/nuget/v3/index.json

Users can still walk back explicitly:

aspire update --channel stable

Validation:

dotnet test --project tests/Aspire.Cli.Tests/Aspire.Cli.Tests.csproj --no-launch-profile -- --filter-class "*.ConfigCommandTests" --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true"
dotnet test --project tests/Aspire.Cli.Tests/Aspire.Cli.Tests.csproj --no-launch-profile -- --filter-method "*.NewCommand_CSharpEmptyTemplateUnderStagingIdentity_WritesStagingNuGetConfig" --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true"

Checklist

  • Is this feature complete?
    • Yes. Ready to ship.
    • No. Follow-up changes expected.
  • Are you including unit tests for the changes and scenario tests if relevant?
    • Yes
    • No
  • Did you add public API?
    • Yes
      • If yes, did you have an API Review for it?
        • Yes
        • No
      • Did you add <remarks /> and <code /> elements on your triple slash comments?
        • Yes
        • No
    • No
  • Does the change make any security assumptions or guarantees?
    • Yes
      • If yes, have you done a threat model and had a security review?
        • Yes
        • No
    • No

A staging-identity Aspire CLI previously did not register the staging package channel unless users also enabled the staging feature flag or configured channel=staging. That caused channel-aware commands such as aspire new to fall back to the implicit package source instead of using staging by default.

Register staging when the running CLI identity is staging, and preserve explicit stable walk-back for guest projects even when package versions are already current. Add regression coverage for new project channel persistence, add/search mixed CLI routes, guest update walk-back, and NuGet.config remapping.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 16, 2026

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 17155

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 17155"

When staging is selected by the running CLI identity or project configuration, default the staging package channel to Both quality so prerelease staging packages are not filtered out by stable-only package searches. Preserve the existing stable default for the feature-flag-only staging path and the stable fallback for invalid quality overrides.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@radical radical marked this pull request as ready for review May 16, 2026 03:16
@radical radical requested a review from mitchdenny as a code owner May 16, 2026 03:16
Copilot AI review requested due to automatic review settings May 16, 2026 03:16
@radical radical requested review from JamesNK and davidfowl as code owners May 16, 2026 03:16
@radical radical requested a review from joperezr May 16, 2026 03:16
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes #17121 so a CLI built with AspireCliChannel=staging automatically registers the staging package channel, and ensures channel pins persist correctly when users explicitly walk back to stable.

Changes:

  • PackagingService.GetChannelsAsync now also registers the staging channel when CliExecutionContext.IdentityChannel == "staging" or when configuration["channel"] == "staging", with quality defaulting to Both (while keeping the safe Stable fallback for invalid override values).
  • GuestAppHostProject.UpdatePackagesAsync persists an explicit channel name even when no package updates are needed, so aspire update --channel stable from a staging-pinned project actually rewrites aspire.config.json#channel.
  • KnownFeatures.IsStagingChannelEnabled remarks updated to remove the now-obsolete "by design, identity does not enable staging" wording.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/Aspire.Cli/Packaging/PackagingService.cs Adds identity-channel trigger for staging registration; threads default-quality into CreateStagingChannel/GetStagingQuality.
src/Aspire.Cli/KnownFeatures.cs Updates the staging-channel remarks to reflect that callers now combine the helper with an identity check.
src/Aspire.Cli/Projects/GuestAppHostProject.cs Persists explicit channel on the up-to-date path; reports UpdatesApplied=true when only the channel pin changes.
tests/Aspire.Cli.Tests/Packaging/PackagingServiceTests.cs New tests for staging-identity registration and config-channel quality/feed defaults.
tests/Aspire.Cli.Tests/Packaging/NuGetConfigMergerTests.cs New test for remapping Aspire.* from staging source to stable source.
tests/Aspire.Cli.Tests/Commands/NewCommandChannelResolutionTests.cs Flips staging-identity expectation from "fall back to Implicit" to "resolve from staging".
tests/Aspire.Cli.Tests/Commands/NewCommandTemplateConfigPersistenceTests.cs Updates staging-identity test to assert channel: staging pinning.
tests/Aspire.Cli.Tests/Commands/AddCommandTests.cs Adds coverage for staging-pinned project under stable CLI and unpinned project under staging CLI.
tests/Aspire.Cli.Tests/Projects/GuestAppHostProjectTests.cs Adds tests for persisting channel: stable on explicit walk-back, both with and without package changes.

radical and others added 6 commits May 18, 2026 13:07
origin/main moved CLI exit codes to CliExitCodes. Update the staging-channel add/search assertions added on this branch to use the current type after merging main.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Cover the combined origin/main update identity fallback and this branch's staging channel registration so an unpinned project updated by a staging CLI resolves the staging channel instead of the implicit/default channel.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Thread requested channel names into PackagingService so stable CLI invocations can materialize staging when --channel or project-local config resolved staging outside the launch directory. Also expose staging in help and self-update prompts for staging-identity CLIs, and refresh the guest update channel persistence comment after the update identity fallback change.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Cover the staging-specific scenarios from the route-crossing analysis: explicit staging update persistence for guest AppHosts, staging NuGet.config materialization for C# empty templates, and project-local staging restore for prebuilt guest AppHosts outside the launch directory.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add a regression test that exercises  and verifies a fresh CLI service provider reads the persisted global value when materializing the staging package channel.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Seed overrideStagingFeed through config set -g, then build a fresh provider before running aspire new so the C# template NuGet.config coverage exercises the persisted global setting instead of direct configuration injection.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@radical radical enabled auto-merge (squash) May 18, 2026 22:55
@radical radical merged commit ceb7452 into microsoft:main May 18, 2026
297 checks passed
@radical radical deleted the radical/issue-17121-staging-channel branch May 18, 2026 23:31
mitchdenny added a commit that referenced this pull request May 19, 2026
* Refuse 'staging' channel synthesis on daily/local/pr CLI builds

Fixes #16652.

PackagingService synthesized the 'staging' channel from the running CLI's
build context: a stable-quality staging used a SHA-specific darc feed built
from the CLI commit, and a Prerelease/Both staging without an override fell
back to the shared daily feed. On a daily, local, or per-PR CLI neither
produces a real staging feed, so 'aspire update --channel staging' silently
resolved to daily package versions.

Gate staging synthesis on the CLI's baked identity (CliExecutionContext.
IdentityChannel from [AssemblyMetadata("AspireCliChannel", ...)]):
  - stable: SHA-specific darc-pub-microsoft-aspire-<hash> feed exists.
  - staging: dogfoods staging packages (per #17155).
  - daily, local, pr-<N>: refuse and surface a localized reason that names
    the running identity and points at 'overrideStagingFeed' or installing a
    staging CLI as recovery paths.

Escape hatches preserved:
  - 'overrideStagingFeed' configuration always allows synthesis.
  - 'StagingChannelEnabled' feature flag continues to opt in for dev/test.

UpdateCommand surfaces the packaging-service reason when --channel staging
is requested but refused, instead of the generic 'No channel found matching
staging' message that hid the actual fix.

When staging IS synthesized, log the resolved feed URL, quality, and pinned
version at Information so users can see what '--channel staging' actually
selected (the 'show what was resolved' suggestion from the issue RCA).

Adds IPackagingService.GetStagingChannelUnavailableReason() and a matching
member on TestPackagingService that defaults to reporting staging as
available so existing tests stay unchanged.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address review feedback: cache staging reason, log once, surface in NewCommand

- Cache GetStagingChannelUnavailableReason via Lazy<string?> so the formatted
  reason is computed once per process instead of on every GetChannelsAsync call.
- Emit the refusal warning and resolved-staging info log at most once per
  process using Interlocked.Exchange flags. Many code paths invoke
  GetChannelsAsync repeatedly (NewCommand, IntegrationPackageSearchService,
  NuGetPackagePrefetcher, etc.); without this, a daily CLI with channel:
  staging pinned in aspire.config.json would spam the warning on every command.
- Surface the staging-specific reason in NewCommand's error path so users
  scaffolding a project with channel: staging configured get the same
  actionable message UpdateCommand now produces.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Refuse staging channel in PrebuiltAppHostServer on daily/local/pr CLIs

Addresses radical's review feedback on #17235. PrebuiltAppHostServer
(and any sibling paths) previously treated a missing requested channel
as a fallback to "all explicit channels", which silently restored
integration packages from the shared daily feed even when the project
pinned channel: staging. Refuse those calls with the same actionable
GetStagingChannelUnavailableReason() the UpdateCommand/NewCommand paths
now use, so the silent-downgrade hole this PR is meant to close is
closed for the bundled AppHost restore path too.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add isolated feature-flag assertion to staging-channel gate test

Addresses JamesNK's review feedback on #17235. The original
feature-flag test set overrideStagingFeed for the test-host
workaround, which short-circuited IsStagingChannelSynthesisAllowed
before the feature-flag branch ever ran. Add a separate assertion
that exercises GetStagingChannelUnavailableReason() on a service
whose only opt-in is the StagingChannelEnabled feature flag, so a
regression that removes the feature-flag branch would now be caught.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Localize remaining inline channel-resolution errors in NewCommand

Addresses JamesNK's review feedback on #17235. The two remaining inline
English literals in NewCommand.ResolveCliTemplateVersionAsync ("No
package channels are available." and "No channel found matching '{0}'.
Valid options are: {1}.") are now sourced from the resource file the
same way the staging-unavailable message already is, so all three
failure paths are localizable.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add StringComparisons.ChannelName and use it for channel-name comparisons

Addresses JamesNK's review feedback on #17235. Introduces a small
StringComparisons helper so the canonical case-insensitive comparison
rule for package-channel names lives in one place, and switches the
existing call sites in PackagingService, UpdateCommand, NewCommand,
PrebuiltAppHostServer, and KnownFeatures from raw
StringComparison.OrdinalIgnoreCase to StringComparisons.ChannelName.
Behaviour is unchanged.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Simplify cached staging-unavailable reason in PackagingService

The previous code lazily allocated a Lazy<T> via Interlocked.CompareExchange,
which is redundant since Lazy<T> already provides thread-safe one-time
initialization. Construct the Lazy eagerly in the constructor and let it do
the deferral, per JamesNK's review on #17235.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Localize "no channel found matching" error in UpdateCommand

Mirrors the same resource extraction that was done for NewCommand earlier
in this PR. Adds NoChannelFoundMatching to UpdateCommandStrings.resx +
Designer, regenerates xlf for all locales, and switches the
ChannelNotFoundException construction site to string.Format against the
resource. Per JamesNK on #17235.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Extract overrideStagingFeed config key to a named constant

Per JamesNK on #17235: replace the duplicated "overrideStagingFeed"
literal in PackagingService (and the test fixtures) with
PackagingService.OverrideStagingFeedConfigKey so a single source of
truth governs the configuration name.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: midenn <midenn@github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Mitch Denny <midenn@orangecake.localdomain>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

aspire new: staging-identity CLI should auto-register the staging channel

3 participants