Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/Aspire.Cli/Commands/AppHostLauncher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ internal static void AddLaunchOptions(Command command)
/// <param name="additionalArgs">Additional unmatched args to forward.</param>
/// <param name="stopAfterLaunchDelay">Optional delay after launch before stopping the AppHost.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <param name="appHostSelected">Callback invoked when an AppHost project file has been selected.</param>
/// <returns>A <see cref="CommandResult"/> indicating success or failure.</returns>
public async Task<CommandResult> LaunchDetachedAsync(
FileInfo? passedAppHostProjectFile,
Expand All @@ -97,7 +98,8 @@ public async Task<CommandResult> LaunchDetachedAsync(
IEnumerable<string> globalArgs,
IEnumerable<string> additionalArgs,
TimeSpan? stopAfterLaunchDelay,
CancellationToken cancellationToken)
CancellationToken cancellationToken,
Action<FileInfo>? appHostSelected = null)
{
// In JSON mode or non-interactive mode, avoid interactive prompts.
var multipleAppHostBehavior = format == OutputFormat.Json || !hostEnvironment.SupportsInteractiveInput
Expand Down Expand Up @@ -126,6 +128,8 @@ public async Task<CommandResult> LaunchDetachedAsync(
return CommandResult.Failure(CliExitCodes.FailedToFindProject);
}

appHostSelected?.Invoke(effectiveAppHostFile);

logger.LogDebug("Starting AppHost in background: {AppHostPath}", effectiveAppHostFile.FullName);

// Check for running instance and stop it if found (same behavior as regular run)
Expand Down
3 changes: 2 additions & 1 deletion src/Aspire.Cli/Commands/BaseCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ internal abstract class BaseCommand : Command
private static readonly int[] s_suppressErrorLogsMessageExitCodes = [CliExitCodes.Cancelled, CliExitCodes.MissingRequiredArgument];

protected virtual bool UpdateNotificationsEnabled { get; } = true;
protected virtual bool IncludeAppHostUpdateCommandInUpdateNotification { get; }

/// <summary>
/// Gets the help group for this command.
Expand Down Expand Up @@ -118,7 +119,7 @@ protected BaseCommand(string name, string description, IFeatures features, ICliU
{
try
{
updateNotifier.NotifyIfUpdateAvailable();
updateNotifier.NotifyIfUpdateAvailable(IncludeAppHostUpdateCommandInUpdateNotification);
}
catch
{
Expand Down
4 changes: 4 additions & 0 deletions src/Aspire.Cli/Commands/RunCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,15 @@ internal sealed class RunCommand : BaseCommand
private readonly ICliHostEnvironment _hostEnvironment;
private readonly ProfilingTelemetry _profilingTelemetry;
private bool _isDetachMode;
private bool _hasAppHostContext;

// Guest AppHosts can bring up the temporary server/backchannel and then fail immediately
// afterward when the guest startup process hits a syntax or pre-execute error. Keep the
// detached parent waiting briefly so those early failures are reported instead of hidden.
private static readonly TimeSpan s_detachedStartupStabilityWindow = TimeSpan.FromSeconds(2);

protected override bool UpdateNotificationsEnabled => !_isDetachMode;
protected override bool IncludeAppHostUpdateCommandInUpdateNotification => _hasAppHostContext;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Also add to StartCommand?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Added this for start too. AppHostLauncher now reports when it has selected an AppHost, so start includes the AppHost guidance only after that context is known.


private static readonly Option<bool> s_detachOption = new("--detach")
{
Expand Down Expand Up @@ -132,6 +134,7 @@ public RunCommand(

protected override async Task<CommandResult> ExecuteAsync(ParseResult parseResult, CancellationToken cancellationToken)
{
_hasAppHostContext = false;
var passedAppHostProjectFile = parseResult.GetValue(AppHostLauncher.s_appHostOption);
var detach = parseResult.GetValue(s_detachOption);
_isDetachMode = detach;
Expand Down Expand Up @@ -220,6 +223,7 @@ protected override async Task<CommandResult> ExecuteAsync(ParseResult parseResul
runActivity?.SetTag(TelemetryConstants.Tags.ErrorType, "project_not_found");
return CommandResult.Failure(CliExitCodes.FailedToFindProject);
}
_hasAppHostContext = true;

// Resolve the language for this file and get the appropriate handler
var project = _projectFactory.TryGetProject(effectiveAppHostFile);
Expand Down
8 changes: 7 additions & 1 deletion src/Aspire.Cli/Commands/StartCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ internal sealed class StartCommand : BaseCommand

private readonly AppHostLauncher _appHostLauncher;
private readonly IConfiguration _configuration;
private bool _hasAppHostContext;

protected override bool IncludeAppHostUpdateCommandInUpdateNotification => _hasAppHostContext;

private static readonly Option<bool> s_noBuildOption = new("--no-build")
{
Expand Down Expand Up @@ -48,6 +51,7 @@ public StartCommand(

protected override async Task<CommandResult> ExecuteAsync(ParseResult parseResult, CancellationToken cancellationToken)
{
_hasAppHostContext = false;
var passedAppHostProjectFile = parseResult.GetValue(AppHostLauncher.s_appHostOption);
var format = parseResult.GetValue(AppHostLauncher.s_formatOption);
var isolated = parseResult.GetValue(AppHostLauncher.s_isolatedOption);
Expand Down Expand Up @@ -75,6 +79,7 @@ protected override async Task<CommandResult> ExecuteAsync(ParseResult parseResul
&& ExtensionHelper.IsExtensionHost(InteractionService, out var extensionInteractionService, out _)
&& string.IsNullOrEmpty(_configuration[KnownConfigNames.ExtensionDebugSessionId]))
{
_hasAppHostContext = passedAppHostProjectFile is not null;
var startDebugSession = parseResult.GetValue(RootCommand.StartDebugSessionOption);
var debugSessionArgs = new List<string>();
if (isolated)
Expand Down Expand Up @@ -140,6 +145,7 @@ await extensionInteractionService.StartDebugSessionAsync(
globalArgs,
additionalArgs,
stopAfterLaunchDelay,
cancellationToken);
cancellationToken,
_ => _hasAppHostContext = true);
}
}
13 changes: 10 additions & 3 deletions src/Aspire.Cli/Interaction/ConsoleInteractionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -693,15 +693,22 @@ public void DisplayEmptyLine()

private const string UpdateUrl = "https://aka.ms/aspire/update";

public void DisplayVersionUpdateNotification(string newerVersion, string? updateCommand = null)
public void DisplayVersionUpdateNotification(string newerVersion, string? updateCommand = null, bool includeAppHostUpdateCommand = false)
{
// Write to stderr to avoid corrupting stdout when JSON output is used
_errorConsole.WriteLine();
_errorConsole.MarkupLine(string.Format(CultureInfo.CurrentCulture, InteractionServiceStrings.NewCliVersionAvailable, newerVersion.EscapeMarkup()));
if (includeAppHostUpdateCommand)
{
_errorConsole.MarkupLine(string.Format(CultureInfo.CurrentCulture, InteractionServiceStrings.NewCliVersionAvailableWithAppHostUpdateCommand, newerVersion.EscapeMarkup(), "aspire update"));
}
else
{
_errorConsole.MarkupLine(string.Format(CultureInfo.CurrentCulture, InteractionServiceStrings.NewCliVersionAvailable, newerVersion.EscapeMarkup()));
}
Comment on lines +700 to +707
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This will only display if the CLI is out of date. And it will display if the CLI is out of date and the app host is up to date.

Really there should be seperate checks for the CLI version and app host version, and then the correct notifications are displayed based on each result.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yep ideally they'd be separate checks, but today we always ship them together. Today it won't display if the CLI is up to date but the apphost is not IIUC. I think this is a good incremental improvement though.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We can get the app host version from the backchannel. It's displayed in aspire ps. We should be able to get it from the app host and compare it with either the installed version of the CLI. Only comparing it with the installed version makes sense because aspire update won't do anything if there is a newer version of the CLI until it is installed.


if (!string.IsNullOrEmpty(updateCommand))
{
_errorConsole.MarkupLine(string.Format(CultureInfo.CurrentCulture, InteractionServiceStrings.ToUpdateRunCommand, updateCommand.EscapeMarkup()));
_errorConsole.MarkupLine(string.Format(CultureInfo.CurrentCulture, InteractionServiceStrings.ToUpdateCliUseCommand, updateCommand.EscapeMarkup()));
}

_errorConsole.MarkupLine(string.Format(CultureInfo.CurrentCulture, InteractionServiceStrings.MoreInfoNewCliVersion, MarkupHelpers.SafeLink(this, UpdateUrl)));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Append this to the end of "A new version of.."? 4 lines is visually heavy.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Updated. The AppHost guidance is now appended to the version line as (use aspire update for this AppHost), so the AppHost case is three lines instead of four.

Expand Down
4 changes: 2 additions & 2 deletions src/Aspire.Cli/Interaction/ExtensionInteractionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -488,9 +488,9 @@ public void DisplayMarkupLine(string markup)
_consoleInteractionService.DisplayMarkupLine(markup);
}

public void DisplayVersionUpdateNotification(string newerVersion, string? updateCommand = null)
public void DisplayVersionUpdateNotification(string newerVersion, string? updateCommand = null, bool includeAppHostUpdateCommand = false)
{
_consoleInteractionService.DisplayVersionUpdateNotification(newerVersion, updateCommand);
_consoleInteractionService.DisplayVersionUpdateNotification(newerVersion, updateCommand, includeAppHostUpdateCommand);
}

public void DisplayRenderable(IRenderable renderable)
Expand Down
2 changes: 1 addition & 1 deletion src/Aspire.Cli/Interaction/IInteractionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ internal interface IInteractionService
/// </summary>
bool SupportsLinks { get; }

void DisplayVersionUpdateNotification(string newerVersion, string? updateCommand = null);
void DisplayVersionUpdateNotification(string newerVersion, string? updateCommand = null, bool includeAppHostUpdateCommand = false);
// The semantic type is stringly-typed because some values originate from backchannel payloads.
// Use ConsoleLogTypes for CLI-defined values.
void WriteConsoleLog(string message, int? lineNumber = null, string? type = null, bool isErrorMessage = false);
Expand Down
15 changes: 12 additions & 3 deletions src/Aspire.Cli/Resources/InteractionServiceStrings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 7 additions & 3 deletions src/Aspire.Cli/Resources/InteractionServiceStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -237,13 +237,17 @@
<value>[yellow]A new version of Aspire is available: {0}[/]</value>
<comment>Do not translate [yellow] and also leave [/] as-is. {0} is the version number</comment>
</data>
<data name="NewCliVersionAvailableWithAppHostUpdateCommand" xml:space="preserve">
<value>[yellow]A new version of Aspire is available: {0}[/] [dim](use {1} for this AppHost)[/]</value>
<comment>Do not translate [yellow], [dim], or [/] as-is. {0} is the version number. {1} is the command to use</comment>
</data>
<data name="MoreInfoNewCliVersion" xml:space="preserve">
<value>[dim]For more information, see: {0}[/]</value>
<comment>Do not translate [dim]. Also leave [/] as-is. {0} is a pre-formatted link</comment>
</data>
<data name="ToUpdateRunCommand" xml:space="preserve">
<value>[dim]To update, run: {0}[/]</value>
<comment>Do not translate [dim]. Also leave [/] as-is. {0} is the command to run</comment>
<data name="ToUpdateCliUseCommand" xml:space="preserve">
<value>[dim]To update the Aspire CLI, use: {0}[/]</value>
<comment>Do not translate [dim]. Also leave [/] as-is. {0} is the command to use</comment>
</data>
<data name="UnbuildableAppHostsDetected" xml:space="preserve">
<value>No AppHosts were found (there may be AppHost project files with syntax errors/invalid SDK versions).</value>
Expand Down
13 changes: 9 additions & 4 deletions src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.cs.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 9 additions & 4 deletions src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.de.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 9 additions & 4 deletions src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.es.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 9 additions & 4 deletions src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.fr.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 9 additions & 4 deletions src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.it.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading