diff --git a/src/Aspire.Cli/Commands/UpdateCommand.cs b/src/Aspire.Cli/Commands/UpdateCommand.cs index f3272a04476..b9ce09b319b 100644 --- a/src/Aspire.Cli/Commands/UpdateCommand.cs +++ b/src/Aspire.Cli/Commands/UpdateCommand.cs @@ -24,6 +24,7 @@ internal sealed class UpdateCommand : BaseCommand private readonly IProjectUpdater _projectUpdater; private readonly ILogger _logger; private readonly ICliDownloader? _cliDownloader; + private readonly ICliUpdateNotifier _updateNotifier; public UpdateCommand( IProjectLocator projectLocator, @@ -41,12 +42,14 @@ public UpdateCommand( ArgumentNullException.ThrowIfNull(packagingService); ArgumentNullException.ThrowIfNull(projectUpdater); ArgumentNullException.ThrowIfNull(logger); + ArgumentNullException.ThrowIfNull(updateNotifier); _projectLocator = projectLocator; _packagingService = packagingService; _projectUpdater = projectUpdater; _logger = logger; _cliDownloader = cliDownloader; + _updateNotifier = updateNotifier; var projectOption = new Option("--project"); projectOption.Description = UpdateCommandStrings.ProjectArgumentDescription; @@ -116,6 +119,21 @@ protected override async Task ExecuteAsync(ParseResult parseResult, Cancell cancellationToken); await _projectUpdater.UpdateProjectAsync(projectFile!, channel, cancellationToken); + + // After successful project update, check if CLI update is available and prompt + if (_cliDownloader is not null && _updateNotifier.IsUpdateAvailable()) + { + var shouldUpdateCli = await InteractionService.ConfirmAsync( + UpdateCommandStrings.UpdateCliAfterProjectUpdatePrompt, + defaultValue: true, + cancellationToken); + + if (shouldUpdateCli) + { + // Use the same channel that was selected for the project update + return await ExecuteSelfUpdateAsync(parseResult, cancellationToken, channel.Name); + } + } } catch (ProjectUpdaterException ex) { @@ -125,15 +143,33 @@ protected override async Task ExecuteAsync(ParseResult parseResult, Cancell } 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)) + { + // Only prompt for self-update if not running as dotnet tool and downloader is available + if (_cliDownloader is not null) + { + var shouldUpdateCli = await InteractionService.ConfirmAsync( + UpdateCommandStrings.NoAppHostFoundUpdateCliPrompt, + defaultValue: true, + cancellationToken); + + if (shouldUpdateCli) + { + return await ExecuteSelfUpdateAsync(parseResult, cancellationToken); + } + } + } + return HandleProjectLocatorException(ex, InteractionService); } return 0; } - private async Task ExecuteSelfUpdateAsync(ParseResult parseResult, CancellationToken cancellationToken) + private async Task ExecuteSelfUpdateAsync(ParseResult parseResult, CancellationToken cancellationToken, string? selectedQuality = null) { - var quality = parseResult.GetValue("--quality"); + var quality = selectedQuality ?? parseResult.GetValue("--quality"); // If quality is not specified, prompt the user if (string.IsNullOrEmpty(quality)) diff --git a/src/Aspire.Cli/Interaction/ConsoleInteractionService.cs b/src/Aspire.Cli/Interaction/ConsoleInteractionService.cs index 01f65a6b816..0ef62dfb683 100644 --- a/src/Aspire.Cli/Interaction/ConsoleInteractionService.cs +++ b/src/Aspire.Cli/Interaction/ConsoleInteractionService.cs @@ -243,10 +243,16 @@ public void DisplayEmptyLine() private const string UpdateUrl = "https://aka.ms/aspire/update"; - public void DisplayVersionUpdateNotification(string newerVersion) + public void DisplayVersionUpdateNotification(string newerVersion, string? updateCommand = null) { _ansiConsole.WriteLine(); _ansiConsole.MarkupLine(string.Format(CultureInfo.CurrentCulture, InteractionServiceStrings.NewCliVersionAvailable, newerVersion)); + + if (!string.IsNullOrEmpty(updateCommand)) + { + _ansiConsole.MarkupLine(string.Format(CultureInfo.CurrentCulture, InteractionServiceStrings.ToUpdateRunCommand, updateCommand)); + } + _ansiConsole.MarkupLine(string.Format(CultureInfo.CurrentCulture, InteractionServiceStrings.MoreInfoNewCliVersion, UpdateUrl)); } } diff --git a/src/Aspire.Cli/Interaction/ExtensionInteractionService.cs b/src/Aspire.Cli/Interaction/ExtensionInteractionService.cs index b782cf45c62..b253c7d5a00 100644 --- a/src/Aspire.Cli/Interaction/ExtensionInteractionService.cs +++ b/src/Aspire.Cli/Interaction/ExtensionInteractionService.cs @@ -308,9 +308,9 @@ public void DisplayMarkdown(string markdown) _consoleInteractionService.DisplayMarkdown(markdown); } - public void DisplayVersionUpdateNotification(string newerVersion) + public void DisplayVersionUpdateNotification(string newerVersion, string? updateCommand = null) { - _consoleInteractionService.DisplayVersionUpdateNotification(newerVersion); + _consoleInteractionService.DisplayVersionUpdateNotification(newerVersion, updateCommand); } public void LogMessage(LogLevel logLevel, string message) diff --git a/src/Aspire.Cli/Interaction/IInteractionService.cs b/src/Aspire.Cli/Interaction/IInteractionService.cs index 0dadd0775a7..50e5dfdaff9 100644 --- a/src/Aspire.Cli/Interaction/IInteractionService.cs +++ b/src/Aspire.Cli/Interaction/IInteractionService.cs @@ -25,6 +25,6 @@ internal interface IInteractionService void DisplayCancellationMessage(); void DisplayEmptyLine(); - void DisplayVersionUpdateNotification(string newerVersion); + void DisplayVersionUpdateNotification(string newerVersion, string? updateCommand = null); void WriteConsoleLog(string message, int? lineNumber = null, string? type = null, bool isErrorMessage = false); } diff --git a/src/Aspire.Cli/Resources/InteractionServiceStrings.Designer.cs b/src/Aspire.Cli/Resources/InteractionServiceStrings.Designer.cs index 600c46cfad0..bb4474823b3 100644 --- a/src/Aspire.Cli/Resources/InteractionServiceStrings.Designer.cs +++ b/src/Aspire.Cli/Resources/InteractionServiceStrings.Designer.cs @@ -338,5 +338,14 @@ public static string WaitingForDebuggerToAttachToAppHost { return ResourceManager.GetString("WaitingForDebuggerToAttachToAppHost", resourceCulture); } } + + /// + /// Looks up a localized string similar to [dim]To update, run: {0}[/]. + /// + public static string ToUpdateRunCommand { + get { + return ResourceManager.GetString("ToUpdateRunCommand", resourceCulture); + } + } } } diff --git a/src/Aspire.Cli/Resources/InteractionServiceStrings.resx b/src/Aspire.Cli/Resources/InteractionServiceStrings.resx index c1409e5776d..c718ff5ca29 100644 --- a/src/Aspire.Cli/Resources/InteractionServiceStrings.resx +++ b/src/Aspire.Cli/Resources/InteractionServiceStrings.resx @@ -208,6 +208,10 @@ [dim]For more information, see: [link]{0}[/][/] Do not translate [dim] and [link]. Also leave [/] as-is. {0} is a URL + + [dim]To update, run: {0}[/] + Do not translate [dim]. Also leave [/] as-is. {0} is the command to run + No app hosts were found (there may be app hosts project files with syntax errors/invalid SDK versions). diff --git a/src/Aspire.Cli/Resources/UpdateCommandStrings.Designer.cs b/src/Aspire.Cli/Resources/UpdateCommandStrings.Designer.cs index e18496b9838..823b05bf397 100644 --- a/src/Aspire.Cli/Resources/UpdateCommandStrings.Designer.cs +++ b/src/Aspire.Cli/Resources/UpdateCommandStrings.Designer.cs @@ -99,5 +99,7 @@ internal static string ProjectArgumentDescription { internal static string MappingRemovedFormat => ResourceManager.GetString("MappingRemovedFormat", resourceCulture); internal static string MappingRetainedFormat => ResourceManager.GetString("MappingRetainedFormat", resourceCulture); internal static string FallbackParsingWarning => ResourceManager.GetString("FallbackParsingWarning", resourceCulture); + internal static string NoAppHostFoundUpdateCliPrompt => ResourceManager.GetString("NoAppHostFoundUpdateCliPrompt", resourceCulture); + internal static string UpdateCliAfterProjectUpdatePrompt => ResourceManager.GetString("UpdateCliAfterProjectUpdatePrompt", resourceCulture); } } diff --git a/src/Aspire.Cli/Resources/UpdateCommandStrings.resx b/src/Aspire.Cli/Resources/UpdateCommandStrings.resx index a7dde2726fd..b008782aa1d 100644 --- a/src/Aspire.Cli/Resources/UpdateCommandStrings.resx +++ b/src/Aspire.Cli/Resources/UpdateCommandStrings.resx @@ -114,4 +114,10 @@ Note: Update plan generated using fallback parsing due to unresolvable AppHost SDK. Dependency analysis may have reduced accuracy. + + No Aspire AppHost project found. Would you like to update the Aspire CLI instead? + + + An update is available for the Aspire CLI. Would you like to update it now? + diff --git a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.cs.xlf b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.cs.xlf index b9392611ebb..71992649c07 100644 --- a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.cs.xlf +++ b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.cs.xlf @@ -137,6 +137,11 @@ Zastavuje se Aspire. + + [dim]To update, run: {0}[/] + [dim]To update, run: {0}[/] + Do not translate [dim]. Also leave [/] as-is. {0} is the command to run + Trusting certificates... Vytváří se vztah důvěryhodnosti k certifikátům... diff --git a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.de.xlf b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.de.xlf index cabbf21608a..88333412831 100644 --- a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.de.xlf +++ b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.de.xlf @@ -137,6 +137,11 @@ Aspire wird beendet. + + [dim]To update, run: {0}[/] + [dim]To update, run: {0}[/] + Do not translate [dim]. Also leave [/] as-is. {0} is the command to run + Trusting certificates... Zertifikate werden als vertrauenswürdig eingestuft... diff --git a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.es.xlf b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.es.xlf index 67c25c40013..d78f3a7571e 100644 --- a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.es.xlf +++ b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.es.xlf @@ -137,6 +137,11 @@ Deteniendo Aspire. + + [dim]To update, run: {0}[/] + [dim]To update, run: {0}[/] + Do not translate [dim]. Also leave [/] as-is. {0} is the command to run + Trusting certificates... Confiar en certificados... diff --git a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.fr.xlf b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.fr.xlf index b5019ce86b4..68c15af41dc 100644 --- a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.fr.xlf +++ b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.fr.xlf @@ -137,6 +137,11 @@ Arrêt d’Aspire. + + [dim]To update, run: {0}[/] + [dim]To update, run: {0}[/] + Do not translate [dim]. Also leave [/] as-is. {0} is the command to run + Trusting certificates... Approbation des certificats... diff --git a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.it.xlf b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.it.xlf index 287d02de9eb..444d6df5fb0 100644 --- a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.it.xlf +++ b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.it.xlf @@ -137,6 +137,11 @@ Arresto di Aspire. + + [dim]To update, run: {0}[/] + [dim]To update, run: {0}[/] + Do not translate [dim]. Also leave [/] as-is. {0} is the command to run + Trusting certificates... Conferma dell'attendibilità dei certificati in corso... diff --git a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.ja.xlf b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.ja.xlf index 0987dd0d29d..dd70e409f19 100644 --- a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.ja.xlf +++ b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.ja.xlf @@ -137,6 +137,11 @@ Aspire を停止しています。 + + [dim]To update, run: {0}[/] + [dim]To update, run: {0}[/] + Do not translate [dim]. Also leave [/] as-is. {0} is the command to run + Trusting certificates... 証明書の信頼を処理中です... diff --git a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.ko.xlf b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.ko.xlf index 1cf27893c44..44bc0ce8e42 100644 --- a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.ko.xlf +++ b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.ko.xlf @@ -137,6 +137,11 @@ Aspire를 중지하는 중입니다. + + [dim]To update, run: {0}[/] + [dim]To update, run: {0}[/] + Do not translate [dim]. Also leave [/] as-is. {0} is the command to run + Trusting certificates... 인증서를 신뢰하는 중... diff --git a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.pl.xlf b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.pl.xlf index 96e907c278a..6e3cf80b63c 100644 --- a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.pl.xlf +++ b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.pl.xlf @@ -137,6 +137,11 @@ Zatrzymywanie platformy Aspire. + + [dim]To update, run: {0}[/] + [dim]To update, run: {0}[/] + Do not translate [dim]. Also leave [/] as-is. {0} is the command to run + Trusting certificates... Ufanie certyfikatom... diff --git a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.pt-BR.xlf b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.pt-BR.xlf index 446fd104ee6..a37f795c2d7 100644 --- a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.pt-BR.xlf +++ b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.pt-BR.xlf @@ -137,6 +137,11 @@ Interrompendo o Aspire. + + [dim]To update, run: {0}[/] + [dim]To update, run: {0}[/] + Do not translate [dim]. Also leave [/] as-is. {0} is the command to run + Trusting certificates... Confiando nos certificados... diff --git a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.ru.xlf b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.ru.xlf index 686c7cd4c10..3190188c29a 100644 --- a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.ru.xlf +++ b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.ru.xlf @@ -137,6 +137,11 @@ Производится остановка Aspire. + + [dim]To update, run: {0}[/] + [dim]To update, run: {0}[/] + Do not translate [dim]. Also leave [/] as-is. {0} is the command to run + Trusting certificates... Выполняется доверие сертификатам... diff --git a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.tr.xlf b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.tr.xlf index 7c641303d64..6aab52c79e5 100644 --- a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.tr.xlf +++ b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.tr.xlf @@ -137,6 +137,11 @@ Aspire durduruluyor. + + [dim]To update, run: {0}[/] + [dim]To update, run: {0}[/] + Do not translate [dim]. Also leave [/] as-is. {0} is the command to run + Trusting certificates... Sertifikalara güveniliyor... diff --git a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.zh-Hans.xlf b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.zh-Hans.xlf index 1b04a2e41ef..62f55aacaa3 100644 --- a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.zh-Hans.xlf +++ b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.zh-Hans.xlf @@ -137,6 +137,11 @@ 正在停止 Aspire。 + + [dim]To update, run: {0}[/] + [dim]To update, run: {0}[/] + Do not translate [dim]. Also leave [/] as-is. {0} is the command to run + Trusting certificates... 正在信任证书... diff --git a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.zh-Hant.xlf b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.zh-Hant.xlf index 4cdef524be3..c65595e6c0a 100644 --- a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.zh-Hant.xlf +++ b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.zh-Hant.xlf @@ -137,6 +137,11 @@ 正在停止 Aspire。 + + [dim]To update, run: {0}[/] + [dim]To update, run: {0}[/] + Do not translate [dim]. Also leave [/] as-is. {0} is the command to run + Trusting certificates... 信任憑證... diff --git a/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.cs.xlf b/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.cs.xlf index bc71025f673..bfe4677fd1a 100644 --- a/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.cs.xlf +++ b/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.cs.xlf @@ -97,6 +97,11 @@ Mapování: {0} + + No Aspire AppHost project found. Would you like to update the Aspire CLI instead? + No Aspire AppHost project found. Would you like to update the Aspire CLI instead? + + No changes detected in NuGet.config V souboru NuGet.config se nezjistily žádné změny. @@ -157,6 +162,11 @@ Neočekávaná cesta kódu. + + An update is available for the Aspire CLI. Would you like to update it now? + An update is available for the Aspire CLI. Would you like to update it now? + + Update package {0} from {1} to {2} Aktualizovat balíček {0} z {1} na {2} diff --git a/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.de.xlf b/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.de.xlf index 37ce2c7e70f..e052eeafc06 100644 --- a/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.de.xlf +++ b/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.de.xlf @@ -97,6 +97,11 @@ Zuordnung: {0} + + No Aspire AppHost project found. Would you like to update the Aspire CLI instead? + No Aspire AppHost project found. Would you like to update the Aspire CLI instead? + + No changes detected in NuGet.config In NuGet.config wurden keine Änderungen festgestellt. @@ -157,6 +162,11 @@ Unerwarteter Codepfad + + An update is available for the Aspire CLI. Would you like to update it now? + An update is available for the Aspire CLI. Would you like to update it now? + + Update package {0} from {1} to {2} Paket {0} von {1} auf {2} aktualisieren diff --git a/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.es.xlf b/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.es.xlf index 0090072ba0e..66d02d56dd6 100644 --- a/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.es.xlf +++ b/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.es.xlf @@ -97,6 +97,11 @@ Asignación: {0} + + No Aspire AppHost project found. Would you like to update the Aspire CLI instead? + No Aspire AppHost project found. Would you like to update the Aspire CLI instead? + + No changes detected in NuGet.config No se detectaron cambios en NuGet.config @@ -157,6 +162,11 @@ Ruta de acceso al código inesperada. + + An update is available for the Aspire CLI. Would you like to update it now? + An update is available for the Aspire CLI. Would you like to update it now? + + Update package {0} from {1} to {2} Actualizar paquete {0} de {1} a {2} diff --git a/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.fr.xlf b/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.fr.xlf index 3cb8ac57456..9a4214d1227 100644 --- a/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.fr.xlf +++ b/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.fr.xlf @@ -97,6 +97,11 @@ Mappage : {0} + + No Aspire AppHost project found. Would you like to update the Aspire CLI instead? + No Aspire AppHost project found. Would you like to update the Aspire CLI instead? + + No changes detected in NuGet.config Aucune modification n’a été détectée dans NuGet.config @@ -157,6 +162,11 @@ Chemin de code inattendu. + + An update is available for the Aspire CLI. Would you like to update it now? + An update is available for the Aspire CLI. Would you like to update it now? + + Update package {0} from {1} to {2} Mettre à jour le package {0} de {1} à {2} diff --git a/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.it.xlf b/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.it.xlf index c8ac74cee07..a9e390bcd4e 100644 --- a/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.it.xlf +++ b/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.it.xlf @@ -97,6 +97,11 @@ Mapping: {0} + + No Aspire AppHost project found. Would you like to update the Aspire CLI instead? + No Aspire AppHost project found. Would you like to update the Aspire CLI instead? + + No changes detected in NuGet.config Nessuna modifica rilevata in NuGet.config @@ -157,6 +162,11 @@ Percorso del codice imprevisto. + + An update is available for the Aspire CLI. Would you like to update it now? + An update is available for the Aspire CLI. Would you like to update it now? + + Update package {0} from {1} to {2} Aggiorna il pacchetto {0} da {1} a {2} diff --git a/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.ja.xlf b/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.ja.xlf index db386eeef55..86c692f7f2d 100644 --- a/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.ja.xlf +++ b/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.ja.xlf @@ -97,6 +97,11 @@ マッピング: {0} + + No Aspire AppHost project found. Would you like to update the Aspire CLI instead? + No Aspire AppHost project found. Would you like to update the Aspire CLI instead? + + No changes detected in NuGet.config NuGet.config に変更は検出されませんでした @@ -157,6 +162,11 @@ 予期しないコード パスです。 + + An update is available for the Aspire CLI. Would you like to update it now? + An update is available for the Aspire CLI. Would you like to update it now? + + Update package {0} from {1} to {2} パッケージ {0} を {1} から {2} に更新する diff --git a/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.ko.xlf b/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.ko.xlf index f7c23864ac9..c50482ded37 100644 --- a/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.ko.xlf +++ b/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.ko.xlf @@ -97,6 +97,11 @@ 매핑: {0} + + No Aspire AppHost project found. Would you like to update the Aspire CLI instead? + No Aspire AppHost project found. Would you like to update the Aspire CLI instead? + + No changes detected in NuGet.config NuGet.config에서 변경 내용이 검색되지 않음 @@ -157,6 +162,11 @@ 예기치 않은 코드 경로입니다. + + An update is available for the Aspire CLI. Would you like to update it now? + An update is available for the Aspire CLI. Would you like to update it now? + + Update package {0} from {1} to {2} 패키지 {0}을(를) {1}에서 {2}(으)로 업데이트 diff --git a/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.pl.xlf b/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.pl.xlf index 727fe1f3fc2..c14c49085c5 100644 --- a/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.pl.xlf +++ b/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.pl.xlf @@ -97,6 +97,11 @@ Mapowanie: {0} + + No Aspire AppHost project found. Would you like to update the Aspire CLI instead? + No Aspire AppHost project found. Would you like to update the Aspire CLI instead? + + No changes detected in NuGet.config Nie wykryto żadnych zmian w pliku NuGet.config @@ -157,6 +162,11 @@ Nieoczekiwana ścieżka w kodzie. + + An update is available for the Aspire CLI. Would you like to update it now? + An update is available for the Aspire CLI. Would you like to update it now? + + Update package {0} from {1} to {2} Aktualizacja {0} z {1} do {2} diff --git a/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.pt-BR.xlf b/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.pt-BR.xlf index a1b80b9a975..31d7822cf09 100644 --- a/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.pt-BR.xlf +++ b/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.pt-BR.xlf @@ -97,6 +97,11 @@ Mapeamento: {0} + + No Aspire AppHost project found. Would you like to update the Aspire CLI instead? + No Aspire AppHost project found. Would you like to update the Aspire CLI instead? + + No changes detected in NuGet.config Nenhuma alteração detectada no NuGet.config @@ -157,6 +162,11 @@ Caminho de código inesperado. + + An update is available for the Aspire CLI. Would you like to update it now? + An update is available for the Aspire CLI. Would you like to update it now? + + Update package {0} from {1} to {2} Atualizar o pacote {0} de {1} para {2} diff --git a/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.ru.xlf b/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.ru.xlf index 450b502695c..d2e6723837f 100644 --- a/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.ru.xlf +++ b/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.ru.xlf @@ -97,6 +97,11 @@ Сопоставление: {0} + + No Aspire AppHost project found. Would you like to update the Aspire CLI instead? + No Aspire AppHost project found. Would you like to update the Aspire CLI instead? + + No changes detected in NuGet.config Изменений в NuGet.config не обнаружено @@ -157,6 +162,11 @@ Непредвиденный путь к коду. + + An update is available for the Aspire CLI. Would you like to update it now? + An update is available for the Aspire CLI. Would you like to update it now? + + Update package {0} from {1} to {2} Обновить пакет {0} с{1} до {2} diff --git a/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.tr.xlf b/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.tr.xlf index 8b53b4fa1d1..ba2e6c4691e 100644 --- a/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.tr.xlf +++ b/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.tr.xlf @@ -97,6 +97,11 @@ Eşleme: {0} + + No Aspire AppHost project found. Would you like to update the Aspire CLI instead? + No Aspire AppHost project found. Would you like to update the Aspire CLI instead? + + No changes detected in NuGet.config NuGet.config dosyasında değişiklik algılanmadı @@ -157,6 +162,11 @@ Beklenmeyen kod yolu. + + An update is available for the Aspire CLI. Would you like to update it now? + An update is available for the Aspire CLI. Would you like to update it now? + + Update package {0} from {1} to {2} {1} olan {0} paketini {2} olarak güncelleyin diff --git a/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.zh-Hans.xlf b/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.zh-Hans.xlf index d0663526544..bf6e2ed0580 100644 --- a/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.zh-Hans.xlf +++ b/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.zh-Hans.xlf @@ -97,6 +97,11 @@ 映射: {0} + + No Aspire AppHost project found. Would you like to update the Aspire CLI instead? + No Aspire AppHost project found. Would you like to update the Aspire CLI instead? + + No changes detected in NuGet.config NuGet.config 中未检测到任何更改 @@ -157,6 +162,11 @@ 意外的代码路径。 + + An update is available for the Aspire CLI. Would you like to update it now? + An update is available for the Aspire CLI. Would you like to update it now? + + Update package {0} from {1} to {2} 将包 {0} 从 {1} 更新为 {2} diff --git a/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.zh-Hant.xlf b/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.zh-Hant.xlf index 11bc2dd1594..f9bf326e05e 100644 --- a/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.zh-Hant.xlf +++ b/src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.zh-Hant.xlf @@ -97,6 +97,11 @@ 對應: {0} + + No Aspire AppHost project found. Would you like to update the Aspire CLI instead? + No Aspire AppHost project found. Would you like to update the Aspire CLI instead? + + No changes detected in NuGet.config 未偵測到 NuGet.config 中的變更 @@ -157,6 +162,11 @@ 未預期的程式碼路徑。 + + An update is available for the Aspire CLI. Would you like to update it now? + An update is available for the Aspire CLI. Would you like to update it now? + + Update package {0} from {1} to {2} 將套件 {0} 從 {1} 更新到 {2} diff --git a/src/Aspire.Cli/Utils/CliUpdateNotifier.cs b/src/Aspire.Cli/Utils/CliUpdateNotifier.cs index 0b39db95c38..31f9a09c649 100644 --- a/src/Aspire.Cli/Utils/CliUpdateNotifier.cs +++ b/src/Aspire.Cli/Utils/CliUpdateNotifier.cs @@ -13,6 +13,7 @@ internal interface ICliUpdateNotifier { Task CheckForCliUpdatesAsync(DirectoryInfo workingDirectory, CancellationToken cancellationToken); void NotifyIfUpdateAvailable(); + bool IsUpdateAvailable(); } internal class CliUpdateNotifier( @@ -49,10 +50,62 @@ public void NotifyIfUpdateAvailable() if (newerVersion is not null) { - interactionService.DisplayVersionUpdateNotification(newerVersion.ToString()); + var updateCommand = IsRunningAsDotNetTool() + ? "dotnet tool update -g Aspire.Cli.Tool" + : "aspire update"; + + interactionService.DisplayVersionUpdateNotification(newerVersion.ToString(), updateCommand); } } + public bool IsUpdateAvailable() + { + if (_availablePackages is null) + { + return false; + } + + var currentVersion = GetCurrentVersion(); + if (currentVersion is null) + { + return false; + } + + var newerVersion = PackageUpdateHelpers.GetNewerVersion(currentVersion, _availablePackages); + return newerVersion is not null; + } + + /// + /// Determines whether the Aspire CLI is running as a .NET tool or as a native binary. + /// + /// + /// true if running as a .NET tool (process name is "dotnet" or "dotnet.exe"); + /// false if running as a native binary (process name is "aspire" or "aspire.exe") or if the process path cannot be determined. + /// + /// + /// This detection is used to determine which update command to display to users: + /// + /// .NET tool installation: "dotnet tool update -g Aspire.Cli.Tool" + /// Native binary installation: "aspire update --self" + /// + /// The detection works by examining , which returns the full path to the current executable. + /// When running as a .NET tool, this path points to the dotnet host executable. When running as a native binary, + /// it points to the aspire executable itself. + /// + 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); + } + protected virtual SemVersion? GetCurrentVersion() { return PackageUpdateHelpers.GetCurrentPackageVersion(); diff --git a/tests/Aspire.Cli.Tests/Commands/NewCommandTests.cs b/tests/Aspire.Cli.Tests/Commands/NewCommandTests.cs index 4a94c66b607..0028707cdc8 100644 --- a/tests/Aspire.Cli.Tests/Commands/NewCommandTests.cs +++ b/tests/Aspire.Cli.Tests/Commands/NewCommandTests.cs @@ -682,5 +682,5 @@ public void DisplayEmptyLine() { } public void DisplayPlainText(string text) { } public void DisplayMarkdown(string markdown) { } public void WriteConsoleLog(string message, int? lineNumber = null, string? type = null, bool isErrorMessage = false) { } - public void DisplayVersionUpdateNotification(string newerVersion) { } + public void DisplayVersionUpdateNotification(string newerVersion, string? updateCommand = null) { } } diff --git a/tests/Aspire.Cli.Tests/Commands/PublishCommandPromptingIntegrationTests.cs b/tests/Aspire.Cli.Tests/Commands/PublishCommandPromptingIntegrationTests.cs index bfa8ccc7597..042f243d3e9 100644 --- a/tests/Aspire.Cli.Tests/Commands/PublishCommandPromptingIntegrationTests.cs +++ b/tests/Aspire.Cli.Tests/Commands/PublishCommandPromptingIntegrationTests.cs @@ -994,7 +994,7 @@ public void DisplayEmptyLine() { } public void DisplayPlainText(string text) { } public void DisplayMarkdown(string markdown) { } - public void DisplayVersionUpdateNotification(string newerVersion) { } + public void DisplayVersionUpdateNotification(string newerVersion, string? updateCommand = null) { } public void WriteConsoleLog(string message, int? lineNumber = null, string? type = null, bool isErrorMessage = false) { diff --git a/tests/Aspire.Cli.Tests/Commands/UpdateCommandTests.cs b/tests/Aspire.Cli.Tests/Commands/UpdateCommandTests.cs index 153b3b7238a..6b42a247ab1 100644 --- a/tests/Aspire.Cli.Tests/Commands/UpdateCommandTests.cs +++ b/tests/Aspire.Cli.Tests/Commands/UpdateCommandTests.cs @@ -5,8 +5,10 @@ using Aspire.Cli.Commands; using Aspire.Cli.Packaging; using Aspire.Cli.Projects; +using Aspire.Cli.Resources; using Aspire.Cli.Tests.TestServices; using Aspire.Cli.Tests.Utils; +using Aspire.Cli.Utils; using Microsoft.Extensions.DependencyInjection; namespace Aspire.Cli.Tests.Commands; @@ -155,6 +157,130 @@ private UpdateCommand CreateUpdateCommand(TemporaryWorkspace workspace) var provider = services.BuildServiceProvider(); return provider.GetRequiredService(); } + + [Fact] + public async Task UpdateCommand_WhenNoProjectFound_PromptsForCliSelfUpdate() + { + using var workspace = TemporaryWorkspace.Create(outputHelper); + + var confirmCallbackInvoked = false; + var services = CliTestHelper.CreateServiceCollection(workspace, outputHelper, options => + { + options.ProjectLocatorFactory = _ => new TestProjectLocator() + { + UseOrFindAppHostProjectFileAsyncCallback = (projectFile, _, _) => + { + // Simulate no project found by throwing ProjectLocatorException + throw new ProjectLocatorException(ErrorStrings.NoProjectFileFound); + } + }; + + options.InteractionServiceFactory = _ => new TestConsoleInteractionService() + { + ConfirmCallback = (prompt, defaultValue) => + { + // Verify the correct prompt is shown + confirmCallbackInvoked = true; + Assert.Contains("Would you like to update the Aspire CLI", prompt); + return false; // User says no + } + }; + + options.DotNetCliRunnerFactory = _ => new TestDotNetCliRunner(); + }); + + var provider = services.BuildServiceProvider(); + + // Act + var command = provider.GetRequiredService(); + var result = command.Parse("update"); + + var exitCode = await result.InvokeAsync().WaitAsync(CliTestConstants.DefaultTimeout); + + // Assert + Assert.True(confirmCallbackInvoked, "Confirm prompt should have been shown"); + Assert.Equal(ExitCodeConstants.FailedToFindProject, exitCode); + } + + [Fact] + public async Task UpdateCommand_WhenProjectUpdatedSuccessfully_PromptsForCliUpdate() + { + using var workspace = TemporaryWorkspace.Create(outputHelper); + + var confirmCallbackInvoked = false; + var services = CliTestHelper.CreateServiceCollection(workspace, outputHelper, options => + { + options.ProjectLocatorFactory = _ => new TestProjectLocator() + { + UseOrFindAppHostProjectFileAsyncCallback = (projectFile, _, _) => + { + return Task.FromResult(new FileInfo(Path.Combine(workspace.WorkspaceRoot.FullName, "AppHost.csproj"))); + } + }; + + options.InteractionServiceFactory = _ => new TestConsoleInteractionService() + { + ConfirmCallback = (prompt, defaultValue) => + { + confirmCallbackInvoked = true; + // Verify the correct prompt is shown after project update + Assert.Contains("An update is available for the Aspire CLI", prompt); + return false; // User says no + } + }; + + options.DotNetCliRunnerFactory = _ => new TestDotNetCliRunner(); + + options.ProjectUpdaterFactory = _ => new TestProjectUpdater() + { + UpdateProjectAsyncCallback = (projectFile, channel, cancellationToken) => + { + return Task.FromResult(new ProjectUpdateResult { UpdatedApplied = true }); + } + }; + + options.PackagingServiceFactory = _ => new TestPackagingService(); + + // Configure update notifier to report that an update is available + options.CliUpdateNotifierFactory = _ => new TestCliUpdateNotifier() + { + IsUpdateAvailableCallback = () => true + }; + }); + + var provider = services.BuildServiceProvider(); + + // Act + var command = provider.GetRequiredService(); + var result = command.Parse("update --project AppHost.csproj"); + + var exitCode = await result.InvokeAsync().WaitAsync(CliTestConstants.DefaultTimeout); + + // Assert + Assert.True(confirmCallbackInvoked, "Confirm prompt should have been shown after successful project update"); + Assert.Equal(0, exitCode); + } +} + +// Test implementation of ICliUpdateNotifier +internal sealed class TestCliUpdateNotifier : ICliUpdateNotifier +{ + public Func? IsUpdateAvailableCallback { get; set; } + + public Task CheckForCliUpdatesAsync(DirectoryInfo workingDirectory, CancellationToken cancellationToken) + { + return Task.CompletedTask; + } + + public void NotifyIfUpdateAvailable() + { + // No-op for tests + } + + public bool IsUpdateAvailable() + { + return IsUpdateAvailableCallback?.Invoke() ?? false; + } } // Test implementation of IProjectUpdater diff --git a/tests/Aspire.Cli.Tests/Templating/DotNetTemplateFactoryTests.cs b/tests/Aspire.Cli.Tests/Templating/DotNetTemplateFactoryTests.cs index 08d2bf3e668..f8a2cfe33f1 100644 --- a/tests/Aspire.Cli.Tests/Templating/DotNetTemplateFactoryTests.cs +++ b/tests/Aspire.Cli.Tests/Templating/DotNetTemplateFactoryTests.cs @@ -396,7 +396,7 @@ public void DisplayPlainText(string text) { } public void DisplayMarkdown(string markdown) { } public void DisplaySubtleMessage(string message, bool escapeMarkup = true) { } public void DisplayEmptyLine() { } - public void DisplayVersionUpdateNotification(string message) { } + public void DisplayVersionUpdateNotification(string message, string? updateCommand = null) { } public void WriteConsoleLog(string message, int? resourceHashCode, string? resourceName, bool isError) { } } diff --git a/tests/Aspire.Cli.Tests/TestServices/TestConsoleInteractionService.cs b/tests/Aspire.Cli.Tests/TestServices/TestConsoleInteractionService.cs index c1ff12f4125..2984fda5e4f 100644 --- a/tests/Aspire.Cli.Tests/TestServices/TestConsoleInteractionService.cs +++ b/tests/Aspire.Cli.Tests/TestServices/TestConsoleInteractionService.cs @@ -107,7 +107,7 @@ public void WriteConsoleLog(string message, int? lineNumber = null, string? type public Action? DisplayVersionUpdateNotificationCallback { get; set; } - public void DisplayVersionUpdateNotification(string newerVersion) + public void DisplayVersionUpdateNotification(string newerVersion, string? updateCommand = null) { DisplayVersionUpdateNotificationCallback?.Invoke(newerVersion); } diff --git a/tests/Aspire.Cli.Tests/TestServices/TestExtensionInteractionService.cs b/tests/Aspire.Cli.Tests/TestServices/TestExtensionInteractionService.cs index 39e75efc7a2..aa96c446035 100644 --- a/tests/Aspire.Cli.Tests/TestServices/TestExtensionInteractionService.cs +++ b/tests/Aspire.Cli.Tests/TestServices/TestExtensionInteractionService.cs @@ -138,7 +138,7 @@ public void WriteConsoleLog(string message, int? lineNumber = null, string? type public Action? DisplayVersionUpdateNotificationCallback { get; set; } - public void DisplayVersionUpdateNotification(string newerVersion) + public void DisplayVersionUpdateNotification(string newerVersion, string? updateCommand = null) { DisplayVersionUpdateNotificationCallback?.Invoke(newerVersion); } diff --git a/tests/agent-scenarios/aspire-update/prompt.md b/tests/agent-scenarios/aspire-update/prompt.md new file mode 100644 index 00000000000..01646174044 --- /dev/null +++ b/tests/agent-scenarios/aspire-update/prompt.md @@ -0,0 +1,369 @@ +# Aspire Update Scenario + +This scenario tests the `aspire update` command functionality, including updating projects across different CLI versions and testing the new CLI self-update prompting feature introduced in this PR. + +## Overview + +This test validates that: +1. The latest released version of the Aspire CLI can be acquired and used to create a new project +2. A new starter application can be created and runs successfully +3. The latest daily build of the Aspire CLI can be acquired and used to update the project, and the updated apphost can be run successfully +4. The PR build of the Aspire CLI can be acquired +5. The `aspire update` command correctly prompts to update the CLI when a newer CLI version is available +6. The CLI can be updated to the daily build through the update prompt +7. The dashboard correctly shows the version from the PR build even when using the daily CLI + +## Prerequisites + +Before starting, ensure you have: +- Docker installed and running (for container-based resources if used) +- Network access to download NuGet packages and CLI builds +- Browser automation tools available (playwright) for verification + +**Note**: The .NET SDK is not required as a prerequisite - the Aspire CLI will install it automatically. + +## Step 1: Download the Latest Released Version of the CLI + +Acquire the latest stable release version of the Aspire CLI. + +**Follow the CLI acquisition instructions already provided in the aspire-playground repository to obtain the latest released version of the Aspire CLI (native AOT build).** + +Once acquired, verify the CLI is installed correctly: + +```bash +aspire --version +``` + +Expected output should show the latest released version number (e.g., `9.5.2` or similar - released versions do not have `-preview` or `-pr` suffixes, though other metadata may be present). + +**Note the version number for comparison in later steps.** + +## Step 2: Create a New Starter Project + +Create a new Aspire starter application using the released CLI version. + +### 2.1 Run the Aspire New Command + +Use `aspire new` with interactive template selection. Choose any template randomly - for this test, we'll use whichever template the agent selects. + +```bash +aspire new +``` + +**Follow the interactive prompts:** +1. Select any starter template (e.g., `aspire-starter` or `aspire-py-starter`) +2. Provide a name for the application (suggestion: `AspireUpdateTest`) +3. Accept default options for framework, frontend, etc. + +### 2.2 Verify Project Creation + +After creation, verify the project structure exists: + +```bash +ls -la +``` + +Expected: Project files and directories should be created successfully. + +## Step 3: Run the AppHost to Verify It Works + +Launch the application to verify it works with the released CLI version. + +### 3.1 Start the Application + +```bash +aspire run +``` + +Wait for the application to start (30-60 seconds). Observe the console output for: +- Dashboard URL with access token +- All resources showing as "Running" +- No critical errors + +### 3.2 Verify Dashboard Access + +Navigate to the dashboard URL (from console output) and perform a minimal check: +- Dashboard loads successfully +- Resources are listed and showing as running + +**Take a screenshot of the dashboard showing resources:** + +```bash +playwright-browser navigate $DASHBOARD_URL +playwright-browser take_screenshot --filename dashboard-released-version.png +``` + +**Note:** This is a minimal verification - we just want to confirm the dashboard launches and displays running resources. Detailed resource checks are not needed here. + +### 3.3 Stop the Application + +Press `Ctrl+C` to stop the application and verify it shuts down cleanly. + +## Step 4: Download the Latest Daily Build of the Aspire CLI + +Acquire the latest daily build version of the Aspire CLI. + +**Follow the CLI acquisition instructions already provided in the aspire-playground repository to obtain the latest daily build of the Aspire CLI (native AOT build).** + +This will replace the released version installed in Step 1. + +## Step 5: Check the Version Number to Verify Installation + +Verify the daily build is now installed: + +```bash +aspire --version +``` + +Expected output should show the daily build version number (e.g., `13.0.0-preview.1.xxxxx` or similar). + +**The version should be different from (and typically newer than) the released version noted in Step 1.** + +## Step 6: Use `aspire update` to Update the AppHost Project + +Update the project to use packages from the daily channel. + +### 6.1 Run aspire update + +```bash +aspire update +``` + +**Follow the interactive prompts:** +1. When prompted to select a channel, choose the **daily** channel +2. Confirm the updates when prompted + +**Observe the update process:** +- Package references being analyzed +- Updates being applied +- NuGet.config being updated +- Successful completion message + +**Note:** Since the project was originally created with the released version and we're now using the daily CLI, the update should find packages to update. + +## Step 7: Run the AppHost to Verify It Worked + +Launch the application again with the updated packages. + +### 7.1 Start the Application + +```bash +aspire run +``` + +Wait for startup and verify all resources are running. + +### 7.2 Navigate to Dashboard Help Menu + +Access the dashboard and check the version information. + +```bash +# Navigate to dashboard +playwright-browser navigate $DASHBOARD_URL + +# Navigate to the Help menu (typically in the top-right) +# Look for version information in the Help menu or About dialog +``` + +**Take a screenshot showing the dashboard version:** + +```bash +playwright-browser take_screenshot --filename dashboard-daily-version.png +``` + +**Verify:** The dashboard version should reflect the daily build packages (matching the CLI version from Step 5). + +### 7.3 Stop the Application + +Press `Ctrl+C` to stop the application. + +## Step 8: Download the PR Build of the CLI + +Acquire the CLI build from this PR. + +**Follow the CLI acquisition instructions already provided in the aspire-playground repository to obtain the PR build of the Aspire CLI (native AOT build).** + +This will replace the daily build installed in Step 4. + +## Step 9: Run `aspire --version` to Verify the Version Number + +Verify the PR build is now installed: + +```bash +aspire --version +``` + +Expected output should show the PR build version number (e.g., `13.0.0-pr.12395.xxxxx` or similar). + +**The version should be different from both the released and daily versions noted earlier.** + +## Step 10: Do `aspire update` to Update the AppHost + +Run the update command again with the PR build CLI. + +### 10.1 Run aspire update + +```bash +aspire update +``` + +**Expected behavior:** +- The project should be detected as up-to-date (no package updates needed since we just updated to daily) +- A prompt should appear: **"An update is available for the Aspire CLI. Would you like to update it now?"** + +**This is the NEW functionality being tested - the CLI detects that a newer version (daily) is available compared to the current PR build and prompts to update the CLI itself.** + +### 10.2 Respond Yes to the Prompt + +When prompted to update the CLI, answer **yes** (or `y`). + +**Observe the CLI self-update process:** +- Current CLI location displayed +- Quality level prompt (select "daily") +- Download progress +- Extraction and installation +- Backup of current CLI +- Success message with new version + +## Step 11: Check `aspire --version` - Should Be Back at Daily Build + +Verify the CLI has been updated back to the daily build: + +```bash +aspire --version +``` + +**Expected output:** The version should now match the daily build version from Step 5 (not the PR build from Step 9). + +**This confirms the CLI self-update functionality worked correctly.** + +## Step 12: Run `aspire run` with Daily CLI, Dashboard Shows PR Build + +Launch the application one more time to verify an important behavior. + +### 12.1 Start the Application + +```bash +aspire run +``` + +Wait for startup and verify all resources are running. + +### 12.2 Check Dashboard Version in Help Menu + +Access the dashboard and check the version information. + +```bash +playwright-browser navigate $DASHBOARD_URL +``` + +Navigate to the Help menu or About section and look for version information. + +**Take a screenshot:** + +```bash +playwright-browser take_screenshot --filename dashboard-pr-build-version.png +``` + +**Expected behavior:** Even though we're using the daily build CLI (Step 11), the dashboard should show the version from the **PR build** packages because that's what the project's packages were last updated to. + +**This demonstrates that:** +1. The CLI version (what's used to run the project) is independent of the package versions (what the project references) +2. Downgrading the CLI doesn't downgrade the project packages +3. The dashboard version reflects the package versions, not the CLI version + +### 12.3 Stop the Application + +Press `Ctrl+C` to stop the application. + +## Step 13: Final Verification Checklist + +Confirm all test objectives were met: + +- [ ] Latest released CLI acquired and version verified +- [ ] New project created successfully with released CLI +- [ ] Application ran successfully with released CLI +- [ ] Dashboard accessible with released version +- [ ] Latest daily build CLI acquired and version verified +- [ ] Project updated to daily channel successfully via `aspire update` +- [ ] Application ran successfully with daily build CLI and updated packages +- [ ] Dashboard showed daily build version after update +- [ ] PR build CLI acquired and version verified +- [ ] `aspire update` with PR build CLI detected project was up-to-date +- [ ] **CLI self-update prompt appeared (NEW FEATURE)** +- [ ] User answered yes to CLI update prompt +- [ ] CLI self-updated back to daily build +- [ ] `aspire --version` confirmed CLI is back at daily build version +- [ ] Application ran with daily build CLI +- [ ] **Dashboard showed PR build version even with daily CLI (important behavior)** + +## Success Criteria + +The test is considered **PASSED** if: + +1. **Released CLI**: Successfully acquired and used to create a working project +2. **Daily CLI**: Successfully acquired and used to update the project to daily channel +3. **PR CLI**: Successfully acquired and detected as older than daily build +4. **Update Prompt**: The CLI correctly prompted to update itself when running `aspire update` with an older CLI version (NEW FEATURE) +5. **Self-Update**: The CLI successfully updated itself to the daily build when user confirmed +6. **Version Independence**: The dashboard correctly showed PR build package version even when running with daily build CLI + +The test is considered **FAILED** if: + +- CLI acquisition fails for any version +- Project creation fails +- Project update fails +- **Update prompt does NOT appear when expected (this is the key new feature being tested)** +- CLI self-update fails or doesn't actually update the CLI +- Dashboard version doesn't correctly reflect package versions + +## Key Testing Points for This PR + +This scenario specifically tests the NEW functionality added in this PR: + +1. **Automatic CLI Update Detection**: After a successful `aspire update` of the project, the CLI checks if a newer CLI version is available +2. **User Prompt**: The CLI prompts the user to update the CLI itself with a clear, actionable message +3. **Self-Update Integration**: Accepting the prompt triggers the `aspire update --self` functionality +4. **Version Awareness**: The CLI correctly compares its own version against available versions + +## Notes for Agent Execution + +When executing this scenario as an automated agent: + +1. **Multiple CLI Versions**: Be prepared to handle multiple CLI installations and version switches +2. **Interactive Prompts**: Pay careful attention to prompts, especially the new CLI update prompt +3. **Version Tracking**: Track and compare version numbers at each step +4. **Screenshots**: Capture dashboard screenshots showing version information +5. **Confirmation**: When the CLI update prompt appears, this is the key moment - it should happen after a successful `aspire update` when using an older CLI version +6. **Expected Flow**: Released → Daily → PR → (update prompt) → Daily +7. **Version Comparison**: The dashboard version should reflect package versions, not CLI version + +## Troubleshooting Tips + +### CLI Update Prompt Doesn't Appear + +If the update prompt doesn't appear when expected: +- Verify the PR build is actually older than the daily build +- Check that the project update completed successfully first +- Ensure the CLI downloader is available (not running as dotnet tool) +- Check console output for any error messages + +### CLI Self-Update Fails + +If the self-update fails: +- Verify network connectivity for downloads +- Check disk space for installation +- Ensure proper permissions for file operations +- Review console output for specific error messages + +### Version Confusion + +If version numbers are confusing: +- Remember: CLI version (what runs the app) ≠ Package version (what the app references) +- The dashboard shows the Aspire.Hosting package version +- The CLI version is shown by `aspire --version` +- After CLI self-update, CLI version changes but package versions remain the same + +--- + +**End of Aspire Update Scenario**