Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
12 changes: 8 additions & 4 deletions src/Aspire.Cli/Commands/BaseCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ protected BaseCommand(string name, string description, IFeatures features, ICliU
result = CommandResult.Failure((int)CliExitCodes.MissingRequiredArgument);
}

var isErrorExitCode = result.ExitCode != CliExitCodes.Success;

if (result.ErrorMessage is not null)
{
interactionService.DisplayError(result.ErrorMessage);
Expand All @@ -74,18 +76,19 @@ protected BaseCommand(string name, string description, IFeatures features, ICliU

if (result.ShouldDisplayCancellationMessage)
{
interactionService.DisplayCancellationMessage();
interactionService.DisplayCancellationMessage(isErrorExitCode ? ConsoleOutput.Error : null);
}

// Display the CLI log file path on non-zero exit codes so the user knows
// where to find diagnostic details. Suppress for user-input errors where
// the log wouldn't contain useful context (e.g., missing required arguments).
if (result.ExitCode != CliExitCodes.Success && result.ExitCode != CliExitCodes.MissingRequiredArgument)
if (isErrorExitCode && result.ExitCode != CliExitCodes.MissingRequiredArgument)
{
interactionService.DisplayMessage(
KnownEmojis.PageFacingUp,
string.Format(CultureInfo.CurrentCulture, InteractionServiceStrings.SeeLogsAt, MarkupHelpers.SafeFileLink(interactionService, executionContext.LogFilePath)),
allowMarkup: true);
allowMarkup: true,
consoleOverride: ConsoleOutput.Error);

// If we connected to a running app host, also display the log file path of
// the CLI process that launched it so users can diagnose issues in both processes.
Expand All @@ -94,7 +97,8 @@ protected BaseCommand(string name, string description, IFeatures features, ICliU
interactionService.DisplayMessage(
KnownEmojis.MagnifyingGlassTiltedLeft,
string.Format(CultureInfo.CurrentCulture, InteractionServiceStrings.SeeAppHostLogsAt, MarkupHelpers.SafeFileLink(interactionService, executionContext.AppHostCliLogFilePath)),
allowMarkup: true);
allowMarkup: true,
consoleOverride: ConsoleOutput.Error);
}
}

Expand Down
28 changes: 21 additions & 7 deletions src/Aspire.Cli/Interaction/ConsoleInteractionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,22 @@ internal class ConsoleInteractionService : IInteractionService
/// <summary>
/// Console used for human-readable messages; routes to stderr when <see cref="Console"/> is set to <see cref="ConsoleOutput.Error"/>.
/// </summary>
private IAnsiConsole MessageConsole => Console == ConsoleOutput.Error ? _errorConsole : _outConsole;
private IAnsiConsole MessageConsole => GetConsoleOutput(null);

// Limit logging to prompts and messages. Don't log raw text output since it may contain sensitive information.
private ILogger MessageLogger => Console == ConsoleOutput.Error ? _stderrLogger : _stdoutLogger;
private ILogger MessageLogger => GetLogger(null);

private IAnsiConsole GetConsoleOutput(ConsoleOutput? consoleOverride) => (consoleOverride ?? Console) switch
{
ConsoleOutput.Error => _errorConsole,
_ => _outConsole
};

private ILogger GetLogger(ConsoleOutput? consoleOverride) => (consoleOverride ?? Console) switch
{
ConsoleOutput.Error => _stderrLogger,
_ => _stdoutLogger
};

public ConsoleOutput Console { get; set; }

Expand Down Expand Up @@ -373,8 +385,10 @@ public void DisplayError(string errorMessage, bool allowMarkup = false)
WriteEmojiMessage(_errorConsole, _stderrLogger, KnownEmojis.CrossMark, $"[red bold]{formatted}[/]", allowMarkup: true);
}

public void DisplayMessage(KnownEmoji emoji, string message, bool allowMarkup = false)
=> WriteEmojiMessage(MessageConsole, MessageLogger, emoji, message, allowMarkup);
public void DisplayMessage(KnownEmoji emoji, string message, bool allowMarkup = false, ConsoleOutput? consoleOverride = null)
{
WriteEmojiMessage(GetConsoleOutput(consoleOverride), GetLogger(consoleOverride), emoji, message, allowMarkup);
}

private static void WriteEmojiMessage(IAnsiConsole target, ILogger logger, KnownEmoji emoji, string message, bool allowMarkup)
{
Expand Down Expand Up @@ -533,10 +547,10 @@ await MessageConsole.Live(initialRenderable)
});
}

public void DisplayCancellationMessage()
public void DisplayCancellationMessage(ConsoleOutput? consoleOverride = null)
{
MessageConsole.WriteLine();
DisplayMessage(KnownEmojis.StopSign, $"[teal bold]{InteractionServiceStrings.StoppingAspire}[/]", allowMarkup: true);
GetConsoleOutput(consoleOverride).WriteLine();
DisplayMessage(KnownEmojis.StopSign, $"[teal bold]{InteractionServiceStrings.StoppingAspire}[/]", allowMarkup: true, consoleOverride: consoleOverride);
}

public async Task<bool> PromptConfirmAsync(string promptText, PromptBinding<bool>? binding = null, CancellationToken cancellationToken = default)
Expand Down
8 changes: 4 additions & 4 deletions src/Aspire.Cli/Interaction/ExtensionInteractionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -352,12 +352,12 @@ public void DisplayError(string errorMessage, bool allowMarkup = false)
Debug.Assert(result);
}

public void DisplayMessage(KnownEmoji emoji, string message, bool allowMarkup = false)
public void DisplayMessage(KnownEmoji emoji, string message, bool allowMarkup = false, ConsoleOutput? consoleOverride = null)
{
var result = _extensionTaskChannel.Writer.TryWrite(async () =>
{
await Backchannel.DisplayMessageAsync(emoji.Name, message.RemoveSpectreFormatting(), _cancellationToken);
_consoleInteractionService.DisplayMessage(emoji, message, allowMarkup);
_consoleInteractionService.DisplayMessage(emoji, message, allowMarkup, consoleOverride);
});
Debug.Assert(result);
}
Expand Down Expand Up @@ -405,11 +405,11 @@ public void DisplayLines(IEnumerable<(OutputLineStream Stream, string Line)> lin
// here would surface every line twice.
}

public void DisplayCancellationMessage()
public void DisplayCancellationMessage(ConsoleOutput? consoleOverride = null)
{
var result = _extensionTaskChannel.Writer.TryWrite(() => Backchannel.DisplayCancellationMessageAsync(_cancellationToken));
Debug.Assert(result);
_consoleInteractionService.DisplayCancellationMessage();
_consoleInteractionService.DisplayCancellationMessage(consoleOverride);
}

public void DisplayEmptyLine()
Expand Down
4 changes: 2 additions & 2 deletions src/Aspire.Cli/Interaction/IInteractionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ internal interface IInteractionService
Task<IReadOnlyList<T>> PromptForSelectionsAsync<T>(string promptText, IEnumerable<T> choices, Func<T, string> choiceFormatter, IEnumerable<T>? preSelected = null, bool optional = false, PromptBinding<string?>? binding = null, bool echoSelected = true, CancellationToken cancellationToken = default) where T : notnull;
int DisplayIncompatibleVersionError(AppHostIncompatibleException ex, string appHostHostingVersion);
void DisplayError(string errorMessage, bool allowMarkup = false);
void DisplayMessage(KnownEmoji emoji, string message, bool allowMarkup = false);
void DisplayMessage(KnownEmoji emoji, string message, bool allowMarkup = false, ConsoleOutput? consoleOverride = null);
void DisplayPlainText(string text);
void DisplayRawText(string text, ConsoleOutput? consoleOverride = null);
void DisplayMarkdown(string markdown, ConsoleOutput? consoleOverride = null, int? maxWidth = null);
Expand All @@ -29,7 +29,7 @@ internal interface IInteractionService
void DisplayLines(IEnumerable<(OutputLineStream Stream, string Line)> lines);
void DisplayRenderable(IRenderable renderable);
Task DisplayLiveAsync(IRenderable initialRenderable, Func<Action<IRenderable>, Task> callback);
void DisplayCancellationMessage();
void DisplayCancellationMessage(ConsoleOutput? consoleOverride = null);
void DisplayEmptyLine();

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -950,11 +950,11 @@ public Task<bool> PromptConfirmAsync(string promptText, PromptBinding<bool>? bin
public void ShowStatus(string statusText, Action action, KnownEmoji? emoji = null, bool allowMarkup = false) => action();
public int DisplayIncompatibleVersionError(AppHostIncompatibleException ex, string appHostHostingVersion) => 0;
public void DisplayError(string errorMessage, bool allowMarkup = false) => DisplayedErrors.Add(errorMessage);
public void DisplayMessage(KnownEmoji emoji, string message, bool allowMarkup = false) { }
public void DisplayMessage(KnownEmoji emoji, string message, bool allowMarkup = false, ConsoleOutput? consoleOverride = null) { }
public void DisplaySuccess(string message, bool allowMarkup = false) { }
public void DisplaySubtleMessage(string message, bool allowMarkup = false) { }
public void DisplayLines(IEnumerable<(OutputLineStream Stream, string Line)> lines) { }
public void DisplayCancellationMessage() { }
public void DisplayCancellationMessage(ConsoleOutput? consoleOverride = null) { }
public void DisplayEmptyLine() { }
public void DisplayPlainText(string text) { }
public void DisplayRawText(string text, ConsoleOutput? consoleOverride = null) { }
Expand Down
6 changes: 3 additions & 3 deletions tests/Aspire.Cli.Tests/Commands/UpdateCommandTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2364,18 +2364,18 @@ public Task<IReadOnlyList<T>> PromptForSelectionsAsync<T>(string promptText, IEn
public int DisplayIncompatibleVersionError(AppHostIncompatibleException ex, string appHostHostingVersion)
=> _innerService.DisplayIncompatibleVersionError(ex, appHostHostingVersion);
public void DisplayError(string errorMessage, bool allowMarkup = false) => _innerService.DisplayError(errorMessage, allowMarkup);
public void DisplayMessage(KnownEmoji emoji, string message, bool allowMarkup = false) => _innerService.DisplayMessage(emoji, message, allowMarkup);
public void DisplayMessage(KnownEmoji emoji, string message, bool allowMarkup = false, ConsoleOutput? consoleOverride = null) => _innerService.DisplayMessage(emoji, message, allowMarkup, consoleOverride);
public void DisplayPlainText(string text) => _innerService.DisplayPlainText(text);
public void DisplayRawText(string text, ConsoleOutput? consoleOverride = null) => _innerService.DisplayRawText(text, consoleOverride);
public void DisplayMarkdown(string markdown, ConsoleOutput? consoleOverride = null, int? maxWidth = null) => _innerService.DisplayMarkdown(markdown, consoleOverride, maxWidth);
public void DisplayMarkupLine(string markup) => _innerService.DisplayMarkupLine(markup);
public void DisplaySuccess(string message, bool allowMarkup = false) => _innerService.DisplaySuccess(message, allowMarkup);
public void DisplaySubtleMessage(string message, bool allowMarkup = false) => _innerService.DisplaySubtleMessage(message, allowMarkup);
public void DisplayLines(IEnumerable<(OutputLineStream Stream, string Line)> lines) => _innerService.DisplayLines(lines);
public void DisplayCancellationMessage()
public void DisplayCancellationMessage(ConsoleOutput? consoleOverride = null)
{
OnCancellationMessageDisplayed?.Invoke();
_innerService.DisplayCancellationMessage();
_innerService.DisplayCancellationMessage(consoleOverride);
}
public void DisplayEmptyLine() => _innerService.DisplayEmptyLine();
public void DisplayVersionUpdateNotification(string newerVersion, string? updateCommand = null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,10 +165,10 @@ public Task LaunchAppHostAsync(string projectFile, List<string> arguments, List<
public Task<IReadOnlyList<T>> PromptForSelectionsAsync<T>(string promptText, IEnumerable<T> choices, Func<T, string> choiceFormatter, IEnumerable<T>? preSelected = null, bool optional = false, PromptBinding<string?>? binding = null, bool echoSelected = true, CancellationToken cancellationToken = default) where T : notnull => throw new NotImplementedException();
public int DisplayIncompatibleVersionError(AppHostIncompatibleException ex, string appHostHostingVersion) => throw new NotImplementedException();
public void DisplayError(string errorMessage, bool allowMarkup = false) => throw new NotImplementedException();
public void DisplayMessage(KnownEmoji emoji, string message, bool allowMarkup = false) => throw new NotImplementedException();
public void DisplayMessage(KnownEmoji emoji, string message, bool allowMarkup = false, ConsoleOutput? consoleOverride = null) => throw new NotImplementedException();
public void DisplaySuccess(string message, bool allowMarkup = false) => throw new NotImplementedException();
public void DisplayLines(IEnumerable<(OutputLineStream Stream, string Line)> lines) => throw new NotImplementedException();
public void DisplayCancellationMessage() => throw new NotImplementedException();
public void DisplayCancellationMessage(ConsoleOutput? consoleOverride = null) => throw new NotImplementedException();
public Task<bool> PromptConfirmAsync(string promptText, PromptBinding<bool>? binding = null, CancellationToken cancellationToken = default) => throw new NotImplementedException();
public void DisplaySubtleMessage(string message, bool allowMarkup = false) => throw new NotImplementedException();
public void DisplayEmptyLine() => throw new NotImplementedException();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -397,9 +397,9 @@ public void ShowStatus(string message, Action work, KnownEmoji? emoji = null, bo

public void DisplaySuccess(string message, bool allowMarkup = false) { }
public void DisplayError(string message, bool allowMarkup = false) { }
public void DisplayMessage(KnownEmoji emoji, string message, bool allowMarkup = false) { }
public void DisplayMessage(KnownEmoji emoji, string message, bool allowMarkup = false, ConsoleOutput? consoleOverride = null) { }
public void DisplayLines(IEnumerable<(OutputLineStream Stream, string Line)> lines) { }
public void DisplayCancellationMessage() { }
public void DisplayCancellationMessage(ConsoleOutput? consoleOverride = null) { }
public int DisplayIncompatibleVersionError(AppHostIncompatibleException ex, string appHostHostingVersion) => 0;
public void DisplayPlainText(string text) { }
public void DisplayRawText(string text, ConsoleOutput? consoleOverride = null) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public void DisplayError(string errorMessage, bool allowMarkup = false)
DisplayErrorCallback?.Invoke(errorMessage);
}

public void DisplayMessage(KnownEmoji emoji, string message, bool allowMarkup = false)
public void DisplayMessage(KnownEmoji emoji, string message, bool allowMarkup = false, ConsoleOutput? consoleOverride = null)
{
}

Expand Down Expand Up @@ -118,7 +118,7 @@ public void DisplayLines(IEnumerable<(OutputLineStream Stream, string Line)> lin
{
}

public void DisplayCancellationMessage()
public void DisplayCancellationMessage(ConsoleOutput? consoleOverride = null)
{
}

Expand Down
4 changes: 2 additions & 2 deletions tests/Aspire.Cli.Tests/TestServices/TestInteractionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ public void DisplayError(string errorMessage, bool allowMarkup = false)
}
}

public void DisplayMessage(KnownEmoji emoji, string message, bool allowMarkup = false)
public void DisplayMessage(KnownEmoji emoji, string message, bool allowMarkup = false, ConsoleOutput? consoleOverride = null)
{
lock (_displayLock)
{
Expand All @@ -223,7 +223,7 @@ public void DisplayLines(IEnumerable<(OutputLineStream Stream, string Line)> lin
}
}

public void DisplayCancellationMessage()
public void DisplayCancellationMessage(ConsoleOutput? consoleOverride = null)
{
DisplayCancellationMessageCount++;
}
Expand Down
Loading