Fix non-interactive self-update channel selection#17512
Conversation
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds non-interactive self-update behavior to avoid channel prompts by inferring the update channel from the CLI’s identity channel (or defaulting to stable), with accompanying tests to validate the selection/override rules.
Changes:
- Update self-update flow to skip channel prompting in
--non-interactivemode and select a channel deterministically. - Prefer
ExecutionContext.IdentityChannelwhen it matches a known update channel; otherwise default tostable. - Add unit tests covering identity-channel matching, local/stale identity channels defaulting, and explicit
--channeloverride.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| tests/Aspire.Cli.Tests/Commands/UpdateCommandTests.cs | Adds tests validating non-interactive self-update channel selection and override behavior. |
| src/Aspire.Cli/Commands/UpdateCommand.cs | Changes non-interactive self-update to bypass prompting and pick a channel based on identity/default logic. |
| if (!string.IsNullOrWhiteSpace(identityChannel) | ||
| && !string.Equals(identityChannel, PackageChannelNames.Local, StringComparisons.ChannelName) | ||
| && channels.Any(c => string.Equals(c, identityChannel, StringComparisons.ChannelName))) | ||
| { | ||
| channel = identityChannel; | ||
| } |
There was a problem hiding this comment.
Good catch. Updated ExecuteSelfUpdateAsync to use channels.FirstOrDefault(...) and assign matchedChannel so the channel string is normalized, consistent with the project-update identity path. Added [InlineData("DAILY", "daily")] to the non-interactive self-update tests.
| await result.InvokeAsync().DefaultTimeout(); | ||
|
|
||
| Assert.False(promptForSelectionInvoked, "Identity-channel match should bypass the channel prompt."); | ||
| Assert.Equal(identityChannel, capturedChannel); |
There was a problem hiding this comment.
We don’t assert exit code Success on these self-update tests because we only fake the download with invalid archive bytes; extraction always fails afterward, so the process exits with an error even when channel selection worked. We assert the channel passed to the downloader (and MissingRequiredArgument when channel is required), same as TracksChannelParameter.
| var services = CliTestHelper.CreateServiceCollection(workspace, outputHelper, options => | ||
| { | ||
| options.CliExecutionContextFactory = _ => workspace.CreateExecutionContext(identityChannel: PackageChannelNames.Local); | ||
|
|
||
| options.InteractionServiceFactory = _ => new TestInteractionService() | ||
| { | ||
| PromptForSelectionCallback = (prompt, choices, formatter, ct) => | ||
| { | ||
| promptForSelectionInvoked = true; | ||
| return PackageChannelNames.Stable; | ||
| } | ||
| }; | ||
|
|
||
| options.CliDownloaderFactory = _ => new TestCliDownloader(workspace.WorkspaceRoot) | ||
| { | ||
| DownloadLatestCliAsyncCallback = (channel, ct) => | ||
| { | ||
| capturedChannel = channel; | ||
| var archivePath = Path.Combine(workspace.WorkspaceRoot.FullName, "test-cli.tar.gz"); | ||
| File.WriteAllText(archivePath, "fake archive"); | ||
| return Task.FromResult(archivePath); | ||
| } | ||
| }; | ||
| }); |
There was a problem hiding this comment.
Matches existing self-update tests (TracksChannelParameter, etc.). I can add RunSelfUpdateAndCaptureChannelAsync in a follow-up, or in this PR if maintainers want that refactor here.
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 17512Or
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 17512" |
|
@davidfowl @mitchdenny I'm not a channel expert. Does it make sense or is it possible to default the channel? |
|
Yep this makes sense |
|
@alirezafzali The one scenario I'm not sure about is this one:
I think there is also an argument to switching to nightly from a PR. Since there isn't an obvious choice, I think in this case the channel should be required. |
…ution. Require --channel when identity does not map to a known update channel (e.g. stale pr-*), keep local defaulting to stable, and assign canonical channel names from the channels list for case-insensitive identity matches.
Makes sense. I’ve changed this so stale/unmatched identities (e.g. pr-99999) require --channel in non-interactive mode instead of defaulting to stable. Known channels still auto-resolve from identity; local still falls back to stable. |
Description
Fixes #15253
aspire update --self --non-interactivefails with an interactive prompt error when--channelis not provided.This updates
ExecuteSelfUpdateAsyncso that in non-interactive mode:stable,daily, orstaging), that channel is used (with canonical casing from the channel list).local, the command defaults tostable.pr-*), the command requires--channelinstead of guessing.Interactive mode still prompts for channel selection as before.
Added tests for:
stableforlocalidentity in non-interactive mode--channelfor stale/unmatched PR identity (pr-99999) in non-interactive mode--channeloverriding the identity channel in non-interactive modeChecklist
<remarks />and<code />elements on your triple slash comments?