diff --git a/src/Aspire.Cli/Commands/AddCommand.cs b/src/Aspire.Cli/Commands/AddCommand.cs index cd8bac92719..ef7bb20a7bb 100644 --- a/src/Aspire.Cli/Commands/AddCommand.cs +++ b/src/Aspire.Cli/Commands/AddCommand.cs @@ -212,12 +212,10 @@ await Parallel.ForEachAsync(channels, cancellationToken, async (channel, ct) => // which prevents 'dotnet add package' from modifying the project. if (_features.IsFeatureEnabled(KnownFeatures.RunningInstanceDetectionEnabled, defaultValue: true)) { - var runningInstanceResult = await InteractionService.ShowStatusAsync( - AddCommandStrings.CheckingForRunningInstances, - async () => await project.FindAndStopRunningInstanceAsync( - effectiveAppHostProjectFile, - ExecutionContext.HomeDirectory, - cancellationToken)); + var runningInstanceResult = await project.FindAndStopRunningInstanceAsync( + effectiveAppHostProjectFile, + ExecutionContext.HomeDirectory, + cancellationToken); if (runningInstanceResult == RunningInstanceResult.InstanceStopped) { diff --git a/src/Aspire.Cli/Commands/AppHostLauncher.cs b/src/Aspire.Cli/Commands/AppHostLauncher.cs index 7ba03516eab..2eeb0a397d0 100644 --- a/src/Aspire.Cli/Commands/AppHostLauncher.cs +++ b/src/Aspire.Cli/Commands/AppHostLauncher.cs @@ -13,7 +13,6 @@ using Aspire.Cli.Resources; using Aspire.Cli.Utils; using Microsoft.Extensions.Logging; -using Spectre.Console; namespace Aspire.Cli.Commands; @@ -27,7 +26,6 @@ internal sealed class AppHostLauncher( CliExecutionContext executionContext, IFeatures features, IInteractionService interactionService, - IAnsiConsole ansiConsole, IAuxiliaryBackchannelMonitor backchannelMonitor, ILogger logger, TimeProvider timeProvider) @@ -122,7 +120,9 @@ public async Task LaunchDetachedAsync( logger.LogDebug("Waiting for socket with prefix: {SocketPrefix}, Hash: {Hash}", expectedSocketPrefix, expectedHash); // Start the child process and wait for the backchannel - var launchResult = await LaunchAndWaitForBackchannelAsync(executablePath, childArgs, expectedHash, cancellationToken); + var launchResult = await interactionService.ShowStatusAsync( + RunCommandStrings.StartingAppHostInBackground, + () => LaunchAndWaitForBackchannelAsync(executablePath, childArgs, expectedHash, cancellationToken)); // Handle failure cases if (launchResult.Backchannel is null || launchResult.ChildProcess is null) @@ -131,7 +131,7 @@ public async Task LaunchDetachedAsync( } // Display results - await DisplayLaunchResultAsync(launchResult, effectiveAppHostFile, childLogFile, format, isExtensionHost, cancellationToken); + DisplayLaunchResult(launchResult, effectiveAppHostFile, childLogFile, format, isExtensionHost); return ExitCodeConstants.Success; } @@ -203,7 +203,7 @@ private async Task StopExistingInstancesAsync(FileInfo effectiveAppHostFile, Can return (dotnetPath, childArgs); } - private record LaunchResult(Process? ChildProcess, IAppHostAuxiliaryBackchannel? Backchannel, bool ChildExitedEarly, int ChildExitCode); + private record LaunchResult(Process? ChildProcess, IAppHostAuxiliaryBackchannel? Backchannel, DashboardUrlsState? DashboardUrls, bool ChildExitedEarly, int ChildExitCode); private async Task LaunchAndWaitForBackchannelAsync( string executablePath, @@ -211,68 +211,66 @@ private async Task LaunchAndWaitForBackchannelAsync( string expectedHash, CancellationToken cancellationToken) { - Process? childProcess = null; - var childExitedEarly = false; - var childExitCode = 0; + Process childProcess; - async Task WaitForBackchannelAsync() + try { - try - { - childProcess = DetachedProcessLauncher.Start( - executablePath, - childArgs, - executionContext.WorkingDirectory.FullName); - } - catch (Exception ex) - { - logger.LogError(ex, "Failed to start child CLI process"); - return null; - } - - logger.LogDebug("Child CLI process started with PID: {PID}", childProcess.Id); + childProcess = DetachedProcessLauncher.Start( + executablePath, + childArgs, + executionContext.WorkingDirectory.FullName); + } + catch (Exception ex) + { + logger.LogError(ex, "Failed to start child CLI process"); + return new LaunchResult(null, null, null, false, 0); + } - var startTime = timeProvider.GetUtcNow(); - var timeout = TimeSpan.FromSeconds(120); + logger.LogDebug("Child CLI process started with PID: {PID}", childProcess.Id); - while (timeProvider.GetUtcNow() - startTime < timeout) - { - cancellationToken.ThrowIfCancellationRequested(); + var startTime = timeProvider.GetUtcNow(); + var timeout = TimeSpan.FromSeconds(120); - if (childProcess.HasExited) - { - childExitedEarly = true; - childExitCode = childProcess.ExitCode; - logger.LogWarning("Child CLI process exited with code {ExitCode}", childExitCode); - return null; - } + while (timeProvider.GetUtcNow() - startTime < timeout) + { + cancellationToken.ThrowIfCancellationRequested(); - await backchannelMonitor.ScanAsync(cancellationToken).ConfigureAwait(false); + if (childProcess.HasExited) + { + var exitCode = childProcess.ExitCode; + logger.LogWarning("Child CLI process exited with code {ExitCode}", exitCode); + return new LaunchResult(childProcess, null, null, true, exitCode); + } - var connection = backchannelMonitor.GetConnectionsByHash(expectedHash).FirstOrDefault(); - if (connection is not null) - { - return connection; - } + await backchannelMonitor.ScanAsync(cancellationToken).ConfigureAwait(false); + var connection = backchannelMonitor.GetConnectionsByHash(expectedHash).FirstOrDefault(); + if (connection is not null) + { + DashboardUrlsState? dashboardUrls = null; try { - await childProcess.WaitForExitAsync(cancellationToken).WaitAsync(TimeSpan.FromMilliseconds(500), cancellationToken).ConfigureAwait(false); + dashboardUrls = await connection.GetDashboardUrlsAsync(cancellationToken).ConfigureAwait(false); } - catch (TimeoutException) + catch (Exception ex) { - // Expected - the 500ms delay elapsed without the process exiting + logger.LogDebug(ex, "Failed to retrieve dashboard URLs from backchannel connection. Continuing without dashboard URLs."); } + + return new LaunchResult(childProcess, connection, dashboardUrls, false, 0); } - return null; + try + { + await childProcess.WaitForExitAsync(cancellationToken).WaitAsync(TimeSpan.FromMilliseconds(500), cancellationToken).ConfigureAwait(false); + } + catch (TimeoutException) + { + // Expected - the 500ms delay elapsed without the process exiting + } } - var backchannel = await interactionService.ShowStatusAsync( - RunCommandStrings.StartingAppHostInBackground, - WaitForBackchannelAsync); - - return new LaunchResult(childProcess, backchannel, childExitedEarly, childExitCode); + return new LaunchResult(childProcess, null, null, false, 0); } private int HandleLaunchFailure(LaunchResult result, string childLogFile) @@ -312,16 +310,15 @@ private int HandleLaunchFailure(LaunchResult result, string childLogFile) return ExitCodeConstants.FailedToDotnetRunAppHost; } - private async Task DisplayLaunchResultAsync( + private void DisplayLaunchResult( LaunchResult result, FileInfo effectiveAppHostFile, string childLogFile, OutputFormat? format, - bool isExtensionHost, - CancellationToken cancellationToken) + bool isExtensionHost) { var appHostInfo = result.Backchannel!.AppHostInfo; - var dashboardUrls = await result.Backchannel.GetDashboardUrlsAsync(cancellationToken).ConfigureAwait(false); + var dashboardUrls = result.DashboardUrls; var pid = appHostInfo?.ProcessId ?? result.ChildProcess!.Id; if (format == OutputFormat.Json) @@ -339,14 +336,14 @@ private async Task DisplayLaunchResultAsync( { var appHostRelativePath = Path.GetRelativePath(executionContext.WorkingDirectory.FullName, effectiveAppHostFile.FullName); RunCommand.RenderAppHostSummary( - ansiConsole, + interactionService, appHostRelativePath, dashboardUrls?.BaseUrlWithLoginToken, codespacesUrl: null, childLogFile, isExtensionHost, pid); - ansiConsole.WriteLine(); + interactionService.DisplayEmptyLine(); interactionService.DisplaySuccess(RunCommandStrings.AppHostStartedSuccessfully); } diff --git a/src/Aspire.Cli/Commands/ExecCommand.cs b/src/Aspire.Cli/Commands/ExecCommand.cs index abf14c52d3f..ba87ea2e045 100644 --- a/src/Aspire.Cli/Commands/ExecCommand.cs +++ b/src/Aspire.Cli/Commands/ExecCommand.cs @@ -13,7 +13,6 @@ using Aspire.Cli.Telemetry; using Aspire.Cli.Utils; using Aspire.Hosting; -using Spectre.Console; namespace Aspire.Cli.Commands; @@ -22,7 +21,6 @@ internal class ExecCommand : BaseCommand private readonly IDotNetCliRunner _runner; private readonly ICertificateService _certificateService; private readonly IProjectLocator _projectLocator; - private readonly IAnsiConsole _ansiConsole; private readonly IDotNetSdkInstaller _sdkInstaller; private static readonly OptionWithLegacy s_appHostOption = new("--apphost", "--project", ExecCommandStrings.ProjectArgumentDescription); @@ -48,7 +46,6 @@ public ExecCommand( IInteractionService interactionService, ICertificateService certificateService, IProjectLocator projectLocator, - IAnsiConsole ansiConsole, AspireCliTelemetry telemetry, IDotNetSdkInstaller sdkInstaller, IFeatures features, @@ -59,7 +56,6 @@ public ExecCommand( _runner = runner; _certificateService = certificateService; _projectLocator = projectLocator; - _ansiConsole = ansiConsole; _sdkInstaller = sdkInstaller; Options.Add(s_appHostOption); diff --git a/src/Aspire.Cli/Commands/RunCommand.cs b/src/Aspire.Cli/Commands/RunCommand.cs index f2e71ee748c..e13252936f2 100644 --- a/src/Aspire.Cli/Commands/RunCommand.cs +++ b/src/Aspire.Cli/Commands/RunCommand.cs @@ -19,6 +19,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Spectre.Console; +using Spectre.Console.Rendering; using StreamJsonRpc; namespace Aspire.Cli.Commands; @@ -58,7 +59,6 @@ internal sealed class RunCommand : BaseCommand private readonly IInteractionService _interactionService; private readonly ICertificateService _certificateService; private readonly IProjectLocator _projectLocator; - private readonly IAnsiConsole _ansiConsole; private readonly IConfiguration _configuration; private readonly IServiceProvider _serviceProvider; private readonly IFeatures _features; @@ -85,7 +85,6 @@ public RunCommand( IInteractionService interactionService, ICertificateService certificateService, IProjectLocator projectLocator, - IAnsiConsole ansiConsole, AspireCliTelemetry telemetry, IConfiguration configuration, IFeatures features, @@ -102,7 +101,6 @@ public RunCommand( _interactionService = interactionService; _certificateService = certificateService; _projectLocator = projectLocator; - _ansiConsole = ansiConsole; _configuration = configuration; _serviceProvider = serviceProvider; _features = features; @@ -206,9 +204,7 @@ protected override async Task ExecuteAsync(ParseResult parseResult, Cancell // Even if we fail to stop we won't block the apphost starting // to make sure we don't ever break flow. It should mostly stop // just fine though. - var runningInstanceResult = await InteractionService.ShowStatusAsync( - RunCommandStrings.CheckingForRunningInstances, - async () => await project.FindAndStopRunningInstanceAsync(effectiveAppHostFile, ExecutionContext.HomeDirectory, cancellationToken)); + var runningInstanceResult = await project.FindAndStopRunningInstanceAsync(effectiveAppHostFile, ExecutionContext.HomeDirectory, cancellationToken); // If in isolated mode and a running instance was stopped, warn the user if (isolated && runningInstanceResult == RunningInstanceResult.InstanceStopped) @@ -276,7 +272,7 @@ protected override async Task ExecuteAsync(ParseResult parseResult, Cancell // Display the UX var appHostRelativePath = Path.GetRelativePath(ExecutionContext.WorkingDirectory.FullName, effectiveAppHostFile.FullName); var longestLocalizedLengthWithColon = RenderAppHostSummary( - _ansiConsole, + InteractionService, appHostRelativePath, dashboardUrls.BaseUrlWithLoginToken, dashboardUrls.CodespacesUrlWithLoginToken, @@ -288,45 +284,69 @@ protected override async Task ExecuteAsync(ParseResult parseResult, Cancell var isRemoteContainers = string.Equals(_configuration["REMOTE_CONTAINERS"], "true", StringComparison.OrdinalIgnoreCase); var isSshRemote = _configuration["VSCODE_IPC_HOOK_CLI"] is not null && _configuration["SSH_CONNECTION"] is not null; + var isRemoteEnvironment = isCodespaces || isRemoteContainers || isSshRemote; - AppendCtrlCMessage(longestLocalizedLengthWithColon); - - if (isCodespaces || isRemoteContainers || isSshRemote) + if (!isRemoteEnvironment) { - bool firstEndpoint = true; + AppendCtrlCMessage(longestLocalizedLengthWithColon); + } + else + { + // We want to display resource information in remote environments. + // Resources update over time so we'll use a live display. + // It is used to show discovered endpoints as they come in over the backchannel. + var discoveredEndpoints = new List<(string Resource, string Endpoint)>(); var endpointsLocalizedString = RunCommandStrings.Endpoints; + var showCtrlC = !ExtensionHelper.IsExtensionHost(_interactionService, out _, out _); - try + IRenderable BuildLiveRenderable() { - var resourceStates = backchannel.GetResourceStatesAsync(cancellationToken); - await foreach (var resourceState in resourceStates.WithCancellation(cancellationToken)) - { - ProcessResourceState(resourceState, (resource, endpoint) => - { - ClearLines(2); - - var endpointsGrid = new Grid(); - endpointsGrid.AddColumn(); - endpointsGrid.AddColumn(); - endpointsGrid.Columns[0].Width = longestLocalizedLengthWithColon; + var rows = new List(); - if (firstEndpoint) - { - endpointsGrid.AddRow(Text.Empty, Text.Empty); - } + if (discoveredEndpoints.Count > 0) + { + var endpointsGrid = new Grid(); + endpointsGrid.AddColumn(); + endpointsGrid.AddColumn(); + endpointsGrid.Columns[0].Width = longestLocalizedLengthWithColon; + endpointsGrid.AddRow(Text.Empty, Text.Empty); + for (var i = 0; i < discoveredEndpoints.Count; i++) + { + var (resource, endpoint) = discoveredEndpoints[i]; endpointsGrid.AddRow( - firstEndpoint ? new Align(new Markup($"[bold green]{endpointsLocalizedString}[/]:"), HorizontalAlignment.Right) : Text.Empty, + i == 0 + ? new Align(new Markup($"[bold green]{endpointsLocalizedString}[/]:"), HorizontalAlignment.Right) + : Text.Empty, new Markup($"[bold]{resource.EscapeMarkup()}[/] [grey]has endpoint[/] [link={endpoint.EscapeMarkup()}]{endpoint.EscapeMarkup()}[/]") ); + } - var endpointsPadder = new Padder(endpointsGrid, new Padding(3, 0)); - _ansiConsole.Write(endpointsPadder); - firstEndpoint = false; + rows.Add(new Padder(endpointsGrid, new Padding(3, 0))); + } - AppendCtrlCMessage(longestLocalizedLengthWithColon); - }); + if (showCtrlC) + { + rows.Add(BuildCtrlCRenderable(longestLocalizedLengthWithColon)); } + + return rows.Count > 0 ? new Rows(rows) : Text.Empty; + } + + try + { + await InteractionService.DisplayLiveAsync(BuildLiveRenderable(), async updateTarget => + { + var resourceStates = backchannel.GetResourceStatesAsync(cancellationToken); + await foreach (var resourceState in resourceStates.WithCancellation(cancellationToken)) + { + ProcessResourceState(resourceState, (resource, endpoint) => + { + discoveredEndpoints.Add((resource, endpoint)); + updateTarget(BuildLiveRenderable()); + }); + } + }); } catch (ConnectionLostException) when (cancellationToken.IsCancellationRequested) { @@ -384,18 +404,16 @@ protected override async Task ExecuteAsync(ParseResult parseResult, Cancell } } - private void ClearLines(int lines) + private static IRenderable BuildCtrlCRenderable(int longestLocalizedLengthWithColon) { - if (lines <= 0) - { - return; - } + var ctrlCGrid = new Grid(); + ctrlCGrid.AddColumn(); + ctrlCGrid.AddColumn(); + ctrlCGrid.Columns[0].Width = longestLocalizedLengthWithColon; + ctrlCGrid.AddRow(Text.Empty, Text.Empty); + ctrlCGrid.AddRow(new Text(string.Empty), new Markup(RunCommandStrings.PressCtrlCToStopAppHost) { Overflow = Overflow.Ellipsis }); - for (var i = 0; i < lines; i++) - { - _ansiConsole.Write("\u001b[1A"); - _ansiConsole.Write("\u001b[2K"); // Clear the line - } + return new Padder(ctrlCGrid, new Padding(3, 0)); } private void AppendCtrlCMessage(int longestLocalizedLengthWithColon) @@ -405,15 +423,7 @@ private void AppendCtrlCMessage(int longestLocalizedLengthWithColon) return; } - var ctrlCGrid = new Grid(); - ctrlCGrid.AddColumn(); - ctrlCGrid.AddColumn(); - ctrlCGrid.Columns[0].Width = longestLocalizedLengthWithColon; - ctrlCGrid.AddRow(Text.Empty, Text.Empty); - ctrlCGrid.AddRow(new Text(string.Empty), new Markup(RunCommandStrings.PressCtrlCToStopAppHost) { Overflow = Overflow.Ellipsis }); - - var ctrlCPadder = new Padder(ctrlCGrid, new Padding(3, 0)); - _ansiConsole.Write(ctrlCPadder); + InteractionService.DisplayRenderable(BuildCtrlCRenderable(longestLocalizedLengthWithColon)); } /// @@ -428,7 +438,7 @@ private void AppendCtrlCMessage(int longestLocalizedLengthWithColon) /// Whether the AppHost is running in the Aspire extension. /// The column width used, for subsequent grid additions. internal static int RenderAppHostSummary( - IAnsiConsole console, + IInteractionService console, string appHostRelativePath, string? dashboardUrl, string? codespacesUrl, @@ -436,7 +446,7 @@ internal static int RenderAppHostSummary( bool isExtensionHost, int? pid = null) { - console.WriteLine(); + console.DisplayEmptyLine(); var grid = new Grid(); grid.AddColumn(); grid.AddColumn(); @@ -501,7 +511,7 @@ internal static int RenderAppHostSummary( } var padder = new Padder(grid, new Padding(3, 0)); - console.Write(padder); + console.DisplayRenderable(padder); return longestLabelLength; } diff --git a/src/Aspire.Cli/Interaction/ConsoleInteractionService.cs b/src/Aspire.Cli/Interaction/ConsoleInteractionService.cs index c3e9188f252..fd89dcffe37 100644 --- a/src/Aspire.Cli/Interaction/ConsoleInteractionService.cs +++ b/src/Aspire.Cli/Interaction/ConsoleInteractionService.cs @@ -309,6 +309,16 @@ public void DisplayRenderable(IRenderable renderable) MessageConsole.Write(renderable); } + public async Task DisplayLiveAsync(IRenderable initialRenderable, Func, Task> callback) + { + await MessageConsole.Live(initialRenderable) + .AutoClear(false) + .StartAsync(async ctx => + { + await callback(renderable => ctx.UpdateTarget(renderable)); + }); + } + public void DisplayCancellationMessage() { MessageConsole.WriteLine(); diff --git a/src/Aspire.Cli/Interaction/ExtensionInteractionService.cs b/src/Aspire.Cli/Interaction/ExtensionInteractionService.cs index f73d543b21f..fcdaa1f706f 100644 --- a/src/Aspire.Cli/Interaction/ExtensionInteractionService.cs +++ b/src/Aspire.Cli/Interaction/ExtensionInteractionService.cs @@ -401,6 +401,11 @@ public void DisplayRenderable(IRenderable renderable) _consoleInteractionService.DisplayRenderable(renderable); } + public Task DisplayLiveAsync(IRenderable initialRenderable, Func, Task> callback) + { + return _consoleInteractionService.DisplayLiveAsync(initialRenderable, callback); + } + public void LogMessage(LogLevel logLevel, string message) { var result = _extensionTaskChannel.Writer.TryWrite(() => Backchannel.LogMessageAsync(logLevel, message.RemoveSpectreFormatting(), _cancellationToken)); diff --git a/src/Aspire.Cli/Interaction/IInteractionService.cs b/src/Aspire.Cli/Interaction/IInteractionService.cs index 69f7c1a7bd4..fc55774caad 100644 --- a/src/Aspire.Cli/Interaction/IInteractionService.cs +++ b/src/Aspire.Cli/Interaction/IInteractionService.cs @@ -27,6 +27,7 @@ internal interface IInteractionService void DisplaySubtleMessage(string message, bool allowMarkup = false); void DisplayLines(IEnumerable<(string Stream, string Line)> lines); void DisplayRenderable(IRenderable renderable); + Task DisplayLiveAsync(IRenderable initialRenderable, Func, Task> callback); void DisplayCancellationMessage(); void DisplayEmptyLine(); diff --git a/src/Aspire.Cli/Projects/ProjectLocator.cs b/src/Aspire.Cli/Projects/ProjectLocator.cs index cbae81bc135..aef5bad337a 100644 --- a/src/Aspire.Cli/Projects/ProjectLocator.cs +++ b/src/Aspire.Cli/Projects/ProjectLocator.cs @@ -255,7 +255,6 @@ public async Task UseOrFindAppHostProjectFileAsync(F logger.LogDebug("No project file specified, searching for apphost projects in {CurrentDirectory}", executionContext.WorkingDirectory); var results = await FindAppHostProjectFilesAsync(executionContext.WorkingDirectory, cancellationToken); - interactionService.DisplayEmptyLine(); logger.LogDebug("Found {ProjectFileCount} project files.", results.BuildableAppHost.Count); diff --git a/src/Aspire.Cli/Resources/AddCommandStrings.Designer.cs b/src/Aspire.Cli/Resources/AddCommandStrings.Designer.cs index 71c85806aaa..f71005d01f8 100644 --- a/src/Aspire.Cli/Resources/AddCommandStrings.Designer.cs +++ b/src/Aspire.Cli/Resources/AddCommandStrings.Designer.cs @@ -205,12 +205,5 @@ public static string UnableToStopRunningInstances } } - public static string CheckingForRunningInstances - { - get - { - return ResourceManager.GetString("CheckingForRunningInstances", resourceCulture); - } - } } } diff --git a/src/Aspire.Cli/Resources/AddCommandStrings.resx b/src/Aspire.Cli/Resources/AddCommandStrings.resx index 1c6d2d5e594..84623e973d3 100644 --- a/src/Aspire.Cli/Resources/AddCommandStrings.resx +++ b/src/Aspire.Cli/Resources/AddCommandStrings.resx @@ -178,7 +178,4 @@ Unable to stop one or more running Aspire AppHost instances. Please stop the application and try again. - - Checking for running instances... - diff --git a/src/Aspire.Cli/Resources/RunCommandStrings.Designer.cs b/src/Aspire.Cli/Resources/RunCommandStrings.Designer.cs index 73fd7e4da7f..519c1ce461b 100644 --- a/src/Aspire.Cli/Resources/RunCommandStrings.Designer.cs +++ b/src/Aspire.Cli/Resources/RunCommandStrings.Designer.cs @@ -207,12 +207,6 @@ public static string RunningInstanceStopped { } } - public static string CheckingForRunningInstances { - get { - return ResourceManager.GetString("CheckingForRunningInstances", resourceCulture); - } - } - public static string DetachArgumentDescription { get { return ResourceManager.GetString("DetachArgumentDescription", resourceCulture); diff --git a/src/Aspire.Cli/Resources/RunCommandStrings.resx b/src/Aspire.Cli/Resources/RunCommandStrings.resx index 293f22bb345..b9e4bc0045b 100644 --- a/src/Aspire.Cli/Resources/RunCommandStrings.resx +++ b/src/Aspire.Cli/Resources/RunCommandStrings.resx @@ -208,9 +208,6 @@ Running instance stopped successfully. - - Checking for running instances... - Starting Aspire apphost in the background... diff --git a/src/Aspire.Cli/Resources/xlf/AddCommandStrings.cs.xlf b/src/Aspire.Cli/Resources/xlf/AddCommandStrings.cs.xlf index 0f8510ed398..bb24461c342 100644 --- a/src/Aspire.Cli/Resources/xlf/AddCommandStrings.cs.xlf +++ b/src/Aspire.Cli/Resources/xlf/AddCommandStrings.cs.xlf @@ -7,11 +7,6 @@ Přidává se integrace hostování Aspire... - - Checking for running instances... - Checking for running instances... - - Add a hosting integration to the apphost. Přidejte do hostitele aplikací Aspire integraci hostování. diff --git a/src/Aspire.Cli/Resources/xlf/AddCommandStrings.de.xlf b/src/Aspire.Cli/Resources/xlf/AddCommandStrings.de.xlf index 14767e1b9d4..d78167fb247 100644 --- a/src/Aspire.Cli/Resources/xlf/AddCommandStrings.de.xlf +++ b/src/Aspire.Cli/Resources/xlf/AddCommandStrings.de.xlf @@ -7,11 +7,6 @@ Aspire-Hosting-Integration wird hinzugefügt... - - Checking for running instances... - Checking for running instances... - - Add a hosting integration to the apphost. Fügen Sie dem Aspire AppHost eine Hosting-Integration hinzu. diff --git a/src/Aspire.Cli/Resources/xlf/AddCommandStrings.es.xlf b/src/Aspire.Cli/Resources/xlf/AddCommandStrings.es.xlf index e4f29db42e6..e338b59f460 100644 --- a/src/Aspire.Cli/Resources/xlf/AddCommandStrings.es.xlf +++ b/src/Aspire.Cli/Resources/xlf/AddCommandStrings.es.xlf @@ -7,11 +7,6 @@ Agregando integración de hospedaje de Aspire... - - Checking for running instances... - Checking for running instances... - - Add a hosting integration to the apphost. Agregue una integración de hospedaje a Aspire AppHost. diff --git a/src/Aspire.Cli/Resources/xlf/AddCommandStrings.fr.xlf b/src/Aspire.Cli/Resources/xlf/AddCommandStrings.fr.xlf index 85411608591..5571a70a18b 100644 --- a/src/Aspire.Cli/Resources/xlf/AddCommandStrings.fr.xlf +++ b/src/Aspire.Cli/Resources/xlf/AddCommandStrings.fr.xlf @@ -7,11 +7,6 @@ Ajout de l’intégration d’hébergement Aspire... - - Checking for running instances... - Checking for running instances... - - Add a hosting integration to the apphost. Ajoutez une intégration d’hébergement à l’Aspire AppHost. diff --git a/src/Aspire.Cli/Resources/xlf/AddCommandStrings.it.xlf b/src/Aspire.Cli/Resources/xlf/AddCommandStrings.it.xlf index 4b58d0c925a..c3247c18f84 100644 --- a/src/Aspire.Cli/Resources/xlf/AddCommandStrings.it.xlf +++ b/src/Aspire.Cli/Resources/xlf/AddCommandStrings.it.xlf @@ -7,11 +7,6 @@ Aggiunta dell'integrazione di hosting Aspire in corso... - - Checking for running instances... - Checking for running instances... - - Add a hosting integration to the apphost. Aggiungere un'integrazione di hosting all'AppHost Aspire. diff --git a/src/Aspire.Cli/Resources/xlf/AddCommandStrings.ja.xlf b/src/Aspire.Cli/Resources/xlf/AddCommandStrings.ja.xlf index 4e1b1aa4004..90add5ffb24 100644 --- a/src/Aspire.Cli/Resources/xlf/AddCommandStrings.ja.xlf +++ b/src/Aspire.Cli/Resources/xlf/AddCommandStrings.ja.xlf @@ -7,11 +7,6 @@ Aspire ホスティング統合を追加しています... - - Checking for running instances... - Checking for running instances... - - Add a hosting integration to the apphost. Aspire AppHost にホスティング統合を追加します。 diff --git a/src/Aspire.Cli/Resources/xlf/AddCommandStrings.ko.xlf b/src/Aspire.Cli/Resources/xlf/AddCommandStrings.ko.xlf index 1fb392ff16e..1c03456afca 100644 --- a/src/Aspire.Cli/Resources/xlf/AddCommandStrings.ko.xlf +++ b/src/Aspire.Cli/Resources/xlf/AddCommandStrings.ko.xlf @@ -7,11 +7,6 @@ Aspire 호스팅 통합을 추가하는 중... - - Checking for running instances... - Checking for running instances... - - Add a hosting integration to the apphost. Aspire AppHost에 호스팅 통합을 추가하세요. diff --git a/src/Aspire.Cli/Resources/xlf/AddCommandStrings.pl.xlf b/src/Aspire.Cli/Resources/xlf/AddCommandStrings.pl.xlf index 80e171960a2..54c15d16df9 100644 --- a/src/Aspire.Cli/Resources/xlf/AddCommandStrings.pl.xlf +++ b/src/Aspire.Cli/Resources/xlf/AddCommandStrings.pl.xlf @@ -7,11 +7,6 @@ Trwa dodawanie integracji hostingu platformy Aspire... - - Checking for running instances... - Checking for running instances... - - Add a hosting integration to the apphost. Dodaj integrację hostingu do hosta AppHost platformy Aspire. diff --git a/src/Aspire.Cli/Resources/xlf/AddCommandStrings.pt-BR.xlf b/src/Aspire.Cli/Resources/xlf/AddCommandStrings.pt-BR.xlf index 174232e941e..5ef5e738086 100644 --- a/src/Aspire.Cli/Resources/xlf/AddCommandStrings.pt-BR.xlf +++ b/src/Aspire.Cli/Resources/xlf/AddCommandStrings.pt-BR.xlf @@ -7,11 +7,6 @@ Adicionando integração de hosting do Aspire... - - Checking for running instances... - Checking for running instances... - - Add a hosting integration to the apphost. Adicione uma integração de hosting ao Aspire AppHost. diff --git a/src/Aspire.Cli/Resources/xlf/AddCommandStrings.ru.xlf b/src/Aspire.Cli/Resources/xlf/AddCommandStrings.ru.xlf index 97af4404f3a..e245c37f520 100644 --- a/src/Aspire.Cli/Resources/xlf/AddCommandStrings.ru.xlf +++ b/src/Aspire.Cli/Resources/xlf/AddCommandStrings.ru.xlf @@ -7,11 +7,6 @@ Добавление интеграции размещения Aspire... - - Checking for running instances... - Checking for running instances... - - Add a hosting integration to the apphost. Добавьте интеграцию размещения в Aspire AppHost. diff --git a/src/Aspire.Cli/Resources/xlf/AddCommandStrings.tr.xlf b/src/Aspire.Cli/Resources/xlf/AddCommandStrings.tr.xlf index 0dfe380f9b3..3b2e6350c61 100644 --- a/src/Aspire.Cli/Resources/xlf/AddCommandStrings.tr.xlf +++ b/src/Aspire.Cli/Resources/xlf/AddCommandStrings.tr.xlf @@ -7,11 +7,6 @@ Aspire barındırma tümleştirmesi ekleniyor... - - Checking for running instances... - Checking for running instances... - - Add a hosting integration to the apphost. Aspire AppHost'a bir barındırma tümleştirmesi ekleyin. diff --git a/src/Aspire.Cli/Resources/xlf/AddCommandStrings.zh-Hans.xlf b/src/Aspire.Cli/Resources/xlf/AddCommandStrings.zh-Hans.xlf index f55a676a7c6..563019e9b67 100644 --- a/src/Aspire.Cli/Resources/xlf/AddCommandStrings.zh-Hans.xlf +++ b/src/Aspire.Cli/Resources/xlf/AddCommandStrings.zh-Hans.xlf @@ -7,11 +7,6 @@ 正在添加 Aspire 托管集成... - - Checking for running instances... - Checking for running instances... - - Add a hosting integration to the apphost. 将托管集成添加到 Aspire AppHost。 diff --git a/src/Aspire.Cli/Resources/xlf/AddCommandStrings.zh-Hant.xlf b/src/Aspire.Cli/Resources/xlf/AddCommandStrings.zh-Hant.xlf index e2c1b243074..1a196dee737 100644 --- a/src/Aspire.Cli/Resources/xlf/AddCommandStrings.zh-Hant.xlf +++ b/src/Aspire.Cli/Resources/xlf/AddCommandStrings.zh-Hant.xlf @@ -7,11 +7,6 @@ 正在新增 Aspire 主機整合... - - Checking for running instances... - Checking for running instances... - - Add a hosting integration to the apphost. 將主機整合新增到 Aspire AppHost。 diff --git a/src/Aspire.Cli/Resources/xlf/RunCommandStrings.cs.xlf b/src/Aspire.Cli/Resources/xlf/RunCommandStrings.cs.xlf index 408f4c0ff76..0e3657221a8 100644 --- a/src/Aspire.Cli/Resources/xlf/RunCommandStrings.cs.xlf +++ b/src/Aspire.Cli/Resources/xlf/RunCommandStrings.cs.xlf @@ -52,11 +52,6 @@ Podrobnosti najdete v protokolech: {0} - - Checking for running instances... - Kontrolují se spuštěné instance... - - Connecting to apphost... Připojování k hostiteli aplikací... diff --git a/src/Aspire.Cli/Resources/xlf/RunCommandStrings.de.xlf b/src/Aspire.Cli/Resources/xlf/RunCommandStrings.de.xlf index fc752ab1cae..06e96c6ebf4 100644 --- a/src/Aspire.Cli/Resources/xlf/RunCommandStrings.de.xlf +++ b/src/Aspire.Cli/Resources/xlf/RunCommandStrings.de.xlf @@ -52,11 +52,6 @@ Details finden Sie in den Protokollen: {0} - - Checking for running instances... - Es wird nach ausgeführten Instanzen gesucht… - - Connecting to apphost... Verbindung zum AppHost wird hergestellt... diff --git a/src/Aspire.Cli/Resources/xlf/RunCommandStrings.es.xlf b/src/Aspire.Cli/Resources/xlf/RunCommandStrings.es.xlf index f8dcdd2a279..b8e6d589e5d 100644 --- a/src/Aspire.Cli/Resources/xlf/RunCommandStrings.es.xlf +++ b/src/Aspire.Cli/Resources/xlf/RunCommandStrings.es.xlf @@ -52,11 +52,6 @@ Compruebe los registros para obtener más información: {0} - - Checking for running instances... - Comprobando si hay instancias en ejecución... - - Connecting to apphost... Conectando con apphost... diff --git a/src/Aspire.Cli/Resources/xlf/RunCommandStrings.fr.xlf b/src/Aspire.Cli/Resources/xlf/RunCommandStrings.fr.xlf index acfc3b0e3c8..a6b974cf238 100644 --- a/src/Aspire.Cli/Resources/xlf/RunCommandStrings.fr.xlf +++ b/src/Aspire.Cli/Resources/xlf/RunCommandStrings.fr.xlf @@ -52,11 +52,6 @@ Pour en savoir plus, consultez les journaux : {0} - - Checking for running instances... - Vérification des instances en cours d’exécution... - - Connecting to apphost... Connexion à apphost... diff --git a/src/Aspire.Cli/Resources/xlf/RunCommandStrings.it.xlf b/src/Aspire.Cli/Resources/xlf/RunCommandStrings.it.xlf index 6b846a3ad4c..014ccaae073 100644 --- a/src/Aspire.Cli/Resources/xlf/RunCommandStrings.it.xlf +++ b/src/Aspire.Cli/Resources/xlf/RunCommandStrings.it.xlf @@ -52,11 +52,6 @@ Controllare i log per i dettagli: {0} - - Checking for running instances... - Verifica delle istanze in esecuzione in corso... - - Connecting to apphost... Connessione all'AppHost Aspire in corso... diff --git a/src/Aspire.Cli/Resources/xlf/RunCommandStrings.ja.xlf b/src/Aspire.Cli/Resources/xlf/RunCommandStrings.ja.xlf index 7bade179271..680d38b946c 100644 --- a/src/Aspire.Cli/Resources/xlf/RunCommandStrings.ja.xlf +++ b/src/Aspire.Cli/Resources/xlf/RunCommandStrings.ja.xlf @@ -52,11 +52,6 @@ 詳細については、ログを確認してください: {0} - - Checking for running instances... - 実行中のインスタンスを確認しています... - - Connecting to apphost... AppHost に接続しています... diff --git a/src/Aspire.Cli/Resources/xlf/RunCommandStrings.ko.xlf b/src/Aspire.Cli/Resources/xlf/RunCommandStrings.ko.xlf index 545d3372808..0fcbfa49490 100644 --- a/src/Aspire.Cli/Resources/xlf/RunCommandStrings.ko.xlf +++ b/src/Aspire.Cli/Resources/xlf/RunCommandStrings.ko.xlf @@ -52,11 +52,6 @@ 자세한 내용은 로그를 확인하세요. {0} - - Checking for running instances... - 실행 중인 인스턴스를 확인하는 중... - - Connecting to apphost... apphost에 연결하는 중... diff --git a/src/Aspire.Cli/Resources/xlf/RunCommandStrings.pl.xlf b/src/Aspire.Cli/Resources/xlf/RunCommandStrings.pl.xlf index cb9b49f296d..c6c78fc36d4 100644 --- a/src/Aspire.Cli/Resources/xlf/RunCommandStrings.pl.xlf +++ b/src/Aspire.Cli/Resources/xlf/RunCommandStrings.pl.xlf @@ -52,11 +52,6 @@ Sprawdź dzienniki, aby uzyskać szczegółowe informacje: {0} - - Checking for running instances... - Sprawdzanie uruchomionych wystąpień... - - Connecting to apphost... Trwa łączenie z hostem AppHost... diff --git a/src/Aspire.Cli/Resources/xlf/RunCommandStrings.pt-BR.xlf b/src/Aspire.Cli/Resources/xlf/RunCommandStrings.pt-BR.xlf index 15b25dbf161..662f51b7e04 100644 --- a/src/Aspire.Cli/Resources/xlf/RunCommandStrings.pt-BR.xlf +++ b/src/Aspire.Cli/Resources/xlf/RunCommandStrings.pt-BR.xlf @@ -52,11 +52,6 @@ Verifique os logs para obter detalhes: {0} - - Checking for running instances... - Verificando se há instâncias em execução... - - Connecting to apphost... Conectando ao apphost... diff --git a/src/Aspire.Cli/Resources/xlf/RunCommandStrings.ru.xlf b/src/Aspire.Cli/Resources/xlf/RunCommandStrings.ru.xlf index acce9cd2a43..384894aef2b 100644 --- a/src/Aspire.Cli/Resources/xlf/RunCommandStrings.ru.xlf +++ b/src/Aspire.Cli/Resources/xlf/RunCommandStrings.ru.xlf @@ -52,11 +52,6 @@ Просмотрите дополнительные сведения в журналах: {0} - - Checking for running instances... - Проверка запущенных экземпляров… - - Connecting to apphost... Подключение к хосту приложений... diff --git a/src/Aspire.Cli/Resources/xlf/RunCommandStrings.tr.xlf b/src/Aspire.Cli/Resources/xlf/RunCommandStrings.tr.xlf index a4f08cabbf1..33eee32031e 100644 --- a/src/Aspire.Cli/Resources/xlf/RunCommandStrings.tr.xlf +++ b/src/Aspire.Cli/Resources/xlf/RunCommandStrings.tr.xlf @@ -52,11 +52,6 @@ Ayrıntılar için günlükleri kontrol edin: {0} - - Checking for running instances... - Çalışan örnekler kontrol ediliyor... - - Connecting to apphost... Apphost'a bağlanılıyor... diff --git a/src/Aspire.Cli/Resources/xlf/RunCommandStrings.zh-Hans.xlf b/src/Aspire.Cli/Resources/xlf/RunCommandStrings.zh-Hans.xlf index e12006ca5e7..f233d92d5b5 100644 --- a/src/Aspire.Cli/Resources/xlf/RunCommandStrings.zh-Hans.xlf +++ b/src/Aspire.Cli/Resources/xlf/RunCommandStrings.zh-Hans.xlf @@ -52,11 +52,6 @@ 请查看日志了解详细信息: {0} - - Checking for running instances... - 正在检查正在运行的实例... - - Connecting to apphost... 正在连接到应用主机... diff --git a/src/Aspire.Cli/Resources/xlf/RunCommandStrings.zh-Hant.xlf b/src/Aspire.Cli/Resources/xlf/RunCommandStrings.zh-Hant.xlf index 9eed9ef8e4a..973f3588808 100644 --- a/src/Aspire.Cli/Resources/xlf/RunCommandStrings.zh-Hant.xlf +++ b/src/Aspire.Cli/Resources/xlf/RunCommandStrings.zh-Hant.xlf @@ -52,11 +52,6 @@ 檢查記錄以取得詳細資料: {0} - - Checking for running instances... - 正在檢查是否有正在執行的執行個體... - - Connecting to apphost... 正線連線到 AppHost... diff --git a/tests/Aspire.Cli.Tests/Commands/NewCommandTests.cs b/tests/Aspire.Cli.Tests/Commands/NewCommandTests.cs index 83c1e43acde..2cc0cdc21e7 100644 --- a/tests/Aspire.Cli.Tests/Commands/NewCommandTests.cs +++ b/tests/Aspire.Cli.Tests/Commands/NewCommandTests.cs @@ -1416,6 +1416,7 @@ public void DisplayMarkupLine(string markup) { } public void WriteConsoleLog(string message, int? lineNumber = null, string? type = null, bool isErrorMessage = false) { } public void DisplayVersionUpdateNotification(string newerVersion, string? updateCommand = null) { } public void DisplayRenderable(IRenderable renderable) { } + public Task DisplayLiveAsync(IRenderable initialRenderable, Func, Task> callback) => callback(_ => { }); } internal sealed class NewCommandTestPackagingService : IPackagingService diff --git a/tests/Aspire.Cli.Tests/Commands/PublishCommandPromptingIntegrationTests.cs b/tests/Aspire.Cli.Tests/Commands/PublishCommandPromptingIntegrationTests.cs index 3e659b2fe27..d32dee8cd71 100644 --- a/tests/Aspire.Cli.Tests/Commands/PublishCommandPromptingIntegrationTests.cs +++ b/tests/Aspire.Cli.Tests/Commands/PublishCommandPromptingIntegrationTests.cs @@ -958,6 +958,7 @@ public void DisplayMarkupLine(string markup) { } public void DisplayVersionUpdateNotification(string newerVersion, string? updateCommand = null) { } public void DisplayRenderable(IRenderable renderable) { } + public Task DisplayLiveAsync(IRenderable initialRenderable, Func, Task> callback) => callback(_ => { }); 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 80e0d0c5e58..aa1d32e3f1a 100644 --- a/tests/Aspire.Cli.Tests/Commands/UpdateCommandTests.cs +++ b/tests/Aspire.Cli.Tests/Commands/UpdateCommandTests.cs @@ -988,6 +988,7 @@ public void DisplayVersionUpdateNotification(string newerVersion, string? update public void WriteConsoleLog(string message, int? lineNumber = null, string? type = null, bool isErrorMessage = false) => _innerService.WriteConsoleLog(message, lineNumber, type, isErrorMessage); public void DisplayRenderable(IRenderable renderable) => _innerService.DisplayRenderable(renderable); + public Task DisplayLiveAsync(IRenderable initialRenderable, Func, Task> callback) => _innerService.DisplayLiveAsync(initialRenderable, callback); } // Test implementation of IProjectUpdater diff --git a/tests/Aspire.Cli.Tests/Templating/DotNetTemplateFactoryTests.cs b/tests/Aspire.Cli.Tests/Templating/DotNetTemplateFactoryTests.cs index f95b721dfe8..fdd0bc788e2 100644 --- a/tests/Aspire.Cli.Tests/Templating/DotNetTemplateFactoryTests.cs +++ b/tests/Aspire.Cli.Tests/Templating/DotNetTemplateFactoryTests.cs @@ -493,6 +493,7 @@ public void DisplayEmptyLine() { } public void DisplayVersionUpdateNotification(string message, string? updateCommand = null) { } public void WriteConsoleLog(string message, int? resourceHashCode, string? resourceName, bool isError) { } public void DisplayRenderable(IRenderable renderable) { } + public Task DisplayLiveAsync(IRenderable initialRenderable, Func, Task> callback) => callback(_ => { }); } private sealed class TestDotNetCliRunner : IDotNetCliRunner diff --git a/tests/Aspire.Cli.Tests/TestServices/TestConsoleInteractionService.cs b/tests/Aspire.Cli.Tests/TestServices/TestConsoleInteractionService.cs index 0bac2dbca30..b06f83018c2 100644 --- a/tests/Aspire.Cli.Tests/TestServices/TestConsoleInteractionService.cs +++ b/tests/Aspire.Cli.Tests/TestServices/TestConsoleInteractionService.cs @@ -148,4 +148,9 @@ public void DisplayVersionUpdateNotification(string newerVersion, string? update public void DisplayRenderable(IRenderable renderable) { } + + public Task DisplayLiveAsync(IRenderable initialRenderable, Func, Task> callback) + { + return callback(_ => { }); + } } diff --git a/tests/Aspire.Cli.Tests/TestServices/TestExtensionInteractionService.cs b/tests/Aspire.Cli.Tests/TestServices/TestExtensionInteractionService.cs index 069a27d9d52..07e82a5609f 100644 --- a/tests/Aspire.Cli.Tests/TestServices/TestExtensionInteractionService.cs +++ b/tests/Aspire.Cli.Tests/TestServices/TestExtensionInteractionService.cs @@ -162,6 +162,11 @@ public void DisplayRenderable(IRenderable renderable) { } + public Task DisplayLiveAsync(IRenderable initialRenderable, Func, Task> callback) + { + return callback(_ => { }); + } + public Action? OpenEditorCallback { get; set; } public void OpenEditor(string projectPath)