Skip to content

Add CLI self-update prompts to aspire update command#12395

Merged
davidfowl merged 9 commits intomainfrom
copilot/enhance-aspire-update-command
Nov 1, 2025
Merged

Add CLI self-update prompts to aspire update command#12395
davidfowl merged 9 commits intomainfrom
copilot/enhance-aspire-update-command

Conversation

Copy link
Contributor

Copilot AI commented Oct 27, 2025

Enhance aspire update command with CLI self-update prompts

Summary

This PR enhances the aspire update command to provide better user experience when updating projects and the CLI itself.

Changes

UpdateCommand prompting logic

  • Catch NoProjectFileFound exception and prompt user to run aspire update --self instead (native CLI only)
  • After successful project update, check ICliUpdateNotifier.IsUpdateAvailable() and prompt to update CLI
  • Invoke ExecuteSelfUpdateAsync() if user confirms either prompt
  • Automatically reuse the channel selected for project update when updating CLI - eliminates redundant channel selection prompt for better UX

BaseCommand notification enhancement

  • CliUpdateNotifier.NotifyIfUpdateAvailable() now detects installation type via Environment.ProcessPath
  • Passes appropriate command to DisplayVersionUpdateNotification():
    • Dotnet tool: dotnet tool update -g Aspire.Cli.Tool
    • Native binary: aspire update (the --self flag is reserved for explicit CLI-only updates and not shown in general notifications)

Interface additions

  • ICliUpdateNotifier.IsUpdateAvailable() - check if update available without displaying notification
  • IInteractionService.DisplayVersionUpdateNotification(string version, string? updateCommand) - optional command parameter

Resource strings

  • UpdateCommandStrings.NoAppHostFoundUpdateCliPrompt
  • UpdateCommandStrings.UpdateCliAfterProjectUpdatePrompt
  • InteractionServiceStrings.ToUpdateRunCommand

Test scenario

  • Added: tests/agent-scenarios/aspire-update/prompt.md - comprehensive test scenario for validating the update functionality across different CLI versions (released → daily → PR build), including testing the new CLI self-update prompts and verifying that package versions are independent of CLI versions
  • Updated to clarify that the updated apphost can be run successfully after the update
  • Removed unnecessary prerequisite about disk space
  • Clarified expected behavior text without PR-specific annotations

Documentation

  • Added comprehensive XML documentation for IsRunningAsDotNetTool() method explaining the detection logic and how it affects user-facing behavior
  • Updated test scenario to clarify that the prompt checks for "newer" versions (not necessarily downgrades) and simplified dashboard verification steps

Example

// When no project found
catch (ProjectLocatorException ex) 
{
    if (ex.Message == ErrorStrings.NoProjectFileFound && _cliDownloader is not null)
    {
        if (await InteractionService.ConfirmAsync(
            UpdateCommandStrings.NoAppHostFoundUpdateCliPrompt, 
            defaultValue: true, cancellationToken))
        {
            return await ExecuteSelfUpdateAsync(parseResult, cancellationToken);
        }
    }
    return HandleProjectLocatorException(ex, InteractionService);
}

// After successful project update, reuse channel for CLI update
if (shouldUpdateCli)
{
    // Use the same channel that was selected for the project update
    return await ExecuteSelfUpdateAsync(parseResult, cancellationToken, channel.Name);
}

All existing tests pass (516 tests). Added tests for both prompt scenarios.

Original prompt

I want to enhance the aspire update command so that if the user runs aspire update and non aspire apphost is detected it assumes that they may have meant aspire update --self and issues a prompt asking if they want to update the Aspire CLI itself.

If they answer yes then the CLI update logic would be invoked. Additionally if aspire update is used and an aspire project is updated I want to prompt to update the Aspire CLI if an update is available, however this prompt would only occur after a succesful project update.

Finally update the notification that we append to all commands (look at BaseCommand). If we are not using the dotnet tool version of the Aspire CLI, show the aspire update --self command in the output text so users know that it is available.


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI changed the title [WIP] Enhance aspire update command with self-update prompt Add CLI self-update prompts to aspire update command Oct 27, 2025
Copilot AI requested a review from mitchdenny October 27, 2025 00:54
@github-actions
Copy link
Contributor

github-actions bot commented Oct 27, 2025

🚀 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/dotnet/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 12395

Or

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

1 similar comment
@github-actions
Copy link
Contributor

🚀 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/dotnet/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 12395

Or

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

@mitchdenny
Copy link
Member

/test-scenario smoke-test-dotnet

@mitchdenny
Copy link
Member

/test-scenario smoke-test-python

@github-actions
Copy link
Contributor

🤖 AI Agent Task Created

Scenario: smoke-test-dotnet

An AI agent has been assigned to execute this scenario.

📝 Issue: https://github.com/dotnet/aspire-playground/issues/49

Please navigate to the issue for more details and to track progress.

@github-actions
Copy link
Contributor

🤖 AI Agent Task Created

Scenario: smoke-test-python

An AI agent has been assigned to execute this scenario.

📝 Issue: https://github.com/dotnet/aspire-playground/issues/50

Please navigate to the issue for more details and to track progress.

@mitchdenny mitchdenny force-pushed the copilot/enhance-aspire-update-command branch from 6ebd507 to f384a8b Compare October 27, 2025 02:57
@mitchdenny
Copy link
Member

/test-scenario smoke-test-dotnet

@mitchdenny
Copy link
Member

/test-scenario smoke-test-python

@github-actions
Copy link
Contributor

🤖 AI Agent Task Created

Scenario: smoke-test-dotnet

An AI agent has been assigned to execute this scenario.

📝 Issue: https://github.com/dotnet/aspire-playground/issues/53

Please navigate to the issue for more details and to track progress.

@github-actions
Copy link
Contributor

🤖 AI Agent Task Created

Scenario: smoke-test-python

An AI agent has been assigned to execute this scenario.

📝 Issue: https://github.com/dotnet/aspire-playground/issues/54

Please navigate to the issue for more details and to track progress.

@mitchdenny
Copy link
Member

This PR adds a few things:

Specific prompt for all commands to use aspire update --self to update the CLI.

image

Interactive prompt after doing aspire update

image

... then:

image

@mitchdenny
Copy link
Member

@copilot as part of this PR I want to create a new test scenario (test scenario prompts are added in /tests/agent-scenarios/[scenario name]/prompt.md

This scenario is a aspire update focused scenario. Here is the test method that needs to be followed:

  1. Download the latest released version of the CLI.
  2. Create a new starter project (doesn't matter which template is chosen - choose randomly)
  3. Run the app host to make sure it works.
  4. Download the latest daily build of the aspire CLI.
  5. Check the version number to make sure it installed.
  6. Use aspire update to update the apphost project to the daily channel.
  7. Run the app host to make sure it worked, you can navigate to the help menu on the dashboard and verify what version of the dashboard code is running.
  8. Download the PR build of the CLI.
  9. Run aspire --version to verify the version number.
  10. Do aspire update to update the apphost.
  11. It should prompt to "upgrade" the CLI back down to the daily version - say yes.
  12. Check aspire --version again it should be back at the daily build verison.
  13. Run aspire run ... even though we are using the daily build of the Aspire CLI, the dashboard help menu should show the PR build of aspire.

Write these up. Look at the other scenarios for the kind of other non-scenario specific details need to be added.

The scenario should be called aspire-update.

@mitchdenny mitchdenny marked this pull request as ready for review October 27, 2025 04:42
Copy link
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

This PR enhances the aspire update command with intelligent CLI self-update prompts, improving user experience when the native CLI distribution is being used. When no AppHost project is found, it prompts users to update the CLI instead. After successfully updating a project, it checks for available CLI updates and offers to apply them. The notification system now detects the installation type (dotnet tool vs. native binary) and displays the appropriate update command.

Key changes:

  • Added two new prompt scenarios in UpdateCommand for CLI self-updates
  • Enhanced CliUpdateNotifier to detect installation type and provide context-aware update commands
  • Extended IInteractionService interface to support optional update command display

Reviewed Changes

Copilot reviewed 39 out of 41 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/Aspire.Cli/Commands/UpdateCommand.cs Implements prompting logic for CLI self-update after project update or when no project found
src/Aspire.Cli/Utils/CliUpdateNotifier.cs Adds installation type detection and IsUpdateAvailable() method
src/Aspire.Cli/Interaction/IInteractionService.cs Extends interface signature to accept optional update command parameter
src/Aspire.Cli/Interaction/ConsoleInteractionService.cs Displays update command when provided
src/Aspire.Cli/Interaction/ExtensionInteractionService.cs Forwards update command to console service
src/Aspire.Cli/Resources/*.resx Adds new resource strings for prompts
src/Aspire.Cli/Resources/xlf/*.xlf Adds localization entries marked as "new" (untranslated)
tests/Aspire.Cli.Tests/Commands/UpdateCommandTests.cs Adds tests for both prompt scenarios with TestCliUpdateNotifier helper
tests/Aspire.Cli.Tests/TestServices/*.cs Updates test service signatures to match interface changes
Files not reviewed (2)
  • src/Aspire.Cli/Resources/InteractionServiceStrings.Designer.cs: Language not supported
  • src/Aspire.Cli/Resources/UpdateCommandStrings.Designer.cs: Language not supported

catch (ProjectLocatorException ex)
{
// Check if this is a "no project found" error and prompt for self-update
if (string.Equals(ex.Message, ErrorStrings.NoProjectFileFound, StringComparisons.CliInputOrOutput))
Copy link

Copilot AI Oct 27, 2025

Choose a reason for hiding this comment

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

The comparison uses StringComparisons.CliInputOrOutput which is likely incorrect. Based on the codebase pattern of comparing error messages, this should use StringComparison.Ordinal or StringComparison.OrdinalIgnoreCase depending on whether case-sensitive comparison is needed for error messages.

Suggested change
if (string.Equals(ex.Message, ErrorStrings.NoProjectFileFound, StringComparisons.CliInputOrOutput))
if (string.Equals(ex.Message, ErrorStrings.NoProjectFileFound, StringComparison.Ordinal))

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The current code using StringComparisons.CliInputOrOutput is correct. This is a standard pattern defined in src/Shared/StringComparers.cs (line 62) where CliInputOrOutput maps to StringComparison.Ordinal, which is the appropriate comparison for CLI error messages. This same pattern is used consistently throughout BaseCommand.cs for all error message comparisons (lines 61, 66, 71, 76, 81), ensuring consistency across the codebase.

Comment on lines +78 to +90
private static bool IsRunningAsDotNetTool()
{
// When running as a dotnet tool, the process path points to "dotnet" or "dotnet.exe"
// When running as a native binary, it points to "aspire" or "aspire.exe"
var processPath = Environment.ProcessPath;
if (string.IsNullOrEmpty(processPath))
{
return false;
}

var fileName = Path.GetFileNameWithoutExtension(processPath);
return string.Equals(fileName, "dotnet", StringComparison.OrdinalIgnoreCase);
}
Copy link

Copilot AI Oct 27, 2025

Choose a reason for hiding this comment

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

Add XML documentation comments to explain the logic for detecting installation type. This is a public-facing behavior that affects which update command users see, so documenting the detection heuristic would help future maintainers understand why Environment.ProcessPath is being used and what the expected values are.

Copilot generated this review using guidance from repository custom instructions.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added comprehensive XML documentation for IsRunningAsDotNetTool() method (commit 884ffef). The documentation explains:

  • The detection logic using Environment.ProcessPath
  • When the method returns true (dotnet tool) vs false (native binary)
  • How this affects the user-facing update command display
  • Expected process names for both installation types

Copy link
Contributor Author

Copilot AI commented Oct 27, 2025

@copilot as part of this PR I want to create a new test scenario (test scenario prompts are added in /tests/agent-scenarios/[scenario name]/prompt.md

This scenario is a aspire update focused scen...

Created the aspire-update test scenario in tests/agent-scenarios/aspire-update/prompt.md (commit e3f2318).

The scenario follows the 13-step test method you outlined:

  1. Downloads latest released CLI
  2. Creates new starter project (random template selection)
  3. Runs the app host to verify
  4. Downloads latest daily build CLI
  5. Verifies version number
  6. Uses aspire update to update to daily channel
  7. Runs app host and verifies dashboard version
  8. Downloads PR build CLI
  9. Verifies version with aspire --version
  10. Runs aspire update - triggers CLI update prompt (new feature)
  11. Accepts prompt to update CLI back to daily
  12. Verifies aspire --version shows daily build
  13. Runs aspire run - dashboard shows PR build version

The scenario includes success criteria, troubleshooting tips, and emphasizes the key new feature being tested: the CLI self-update prompt that appears after a successful project update when a newer CLI version is available.

@davidfowl
Copy link
Member

@mitchdenny When using the dotnet CLI tool it should not suggest update --self. See #12391

@mitchdenny
Copy link
Member

@copilot tests/agent-scenarios/aspire-update/prompt.md:13 the goal is not necessarily that it is downgraded, it is that it prompts if there is a newer version available.

Technically the daily build is probably older than the PR build, but its version number sorts semantically higher so its considered an upgrade.
tests/agent-scenarios/aspire-update/prompt.md:38

Copy link
Contributor Author

Copilot AI commented Oct 27, 2025

@copilot tests/agent-scenarios/aspire-update/prompt.md:13 the goal is not necessarily that it is downgraded, it is that it prompts if there is a newer version available.

Technically the daily build i...

Updated the test scenario wording (commit 884ffef) to clarify:

  • Changed "downgraded" to "updated" - the prompt checks for newer CLI versions (semantically higher version numbers)
  • Updated expected released version example to 9.5.2 and noted that released versions don't have -preview or -pr suffixes
  • Simplified Step 3.2 dashboard verification to be a minimal check (just confirm dashboard launches and shows running resources)

Copilot AI and others added 8 commits November 1, 2025 16:42
…ompts

Co-authored-by: mitchdenny <513398+mitchdenny@users.noreply.github.com>
Co-authored-by: mitchdenny <513398+mitchdenny@users.noreply.github.com>
Co-authored-by: mitchdenny <513398+mitchdenny@users.noreply.github.com>
…ario wording

Co-authored-by: mitchdenny <513398+mitchdenny@users.noreply.github.com>
…naries

Co-authored-by: mitchdenny <513398+mitchdenny@users.noreply.github.com>
…rio overview

Co-authored-by: mitchdenny <513398+mitchdenny@users.noreply.github.com>
… test scenario

Co-authored-by: mitchdenny <513398+mitchdenny@users.noreply.github.com>
@mitchdenny mitchdenny force-pushed the copilot/enhance-aspire-update-command branch from b91f215 to be5951f Compare November 1, 2025 05:44
@davidfowl
Copy link
Member

The only weird thing is that we detect an update for the CLI and then we ask you what channel you want to update from (shouldn't it know?)

@davidfowl
Copy link
Member

@copilot is it possible to tell what channel to apply the update from based on the update being detected? Right now asks the user again what channel to update the CLI for. We should use the one from the original update selection.

… re-prompting

Co-authored-by: davidfowl <95136+davidfowl@users.noreply.github.com>
auto-merge was automatically disabled November 1, 2025 07:27

Head branch was pushed to by a user without write access

Copy link
Contributor Author

Copilot AI commented Nov 1, 2025

@copilot is it possible to tell what channel to apply the update from based on the update being detected? Right now asks the user again what channel to update the CLI for. We should use the one from t...

Modified ExecuteSelfUpdateAsync to accept an optional selectedQuality parameter (commit 407710b). When the CLI update prompt appears after a successful project update, the method now automatically uses the same channel that was selected for the project update, eliminating the redundant channel selection prompt. The user will now see:

An update is available for the Aspire CLI. Would you like to update it now? [y/n] (y): y
Current CLI location: C:\Users\...\aspire.exe
Updating to quality level: daily

Without being prompted again to select the channel, since it reuses the "daily" channel from the project update.

@davidfowl davidfowl merged commit 99df706 into main Nov 1, 2025
587 of 591 checks passed
@davidfowl davidfowl deleted the copilot/enhance-aspire-update-command branch November 1, 2025 08:23
@davidfowl
Copy link
Member

/backport to release/13.0

@github-actions
Copy link
Contributor

github-actions bot commented Nov 1, 2025

Started backporting to release/13.0: https://github.com/dotnet/aspire/actions/runs/18994033122

@dotnet-policy-service dotnet-policy-service bot modified the milestones: 13.0, 13.1 Nov 1, 2025
@github-actions github-actions bot locked and limited conversation to collaborators Dec 1, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants