Skip to content
Merged
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
14 changes: 9 additions & 5 deletions src/Aspire.Cli/Interaction/ConsoleInteractionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -369,17 +369,21 @@ public int DisplayIncompatibleVersionError(AppHostIncompatibleException ex, stri
public void DisplayError(string errorMessage, bool allowMarkup = false)
{
var formatted = allowMarkup ? errorMessage : errorMessage.EscapeMarkup();
DisplayMessage(KnownEmojis.CrossMark, $"[red bold]{formatted}[/]", allowMarkup: true);
// Always write errors to stderr so callers can capture them separately from stdout.
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);

private static void WriteEmojiMessage(IAnsiConsole target, ILogger logger, KnownEmoji emoji, string message, bool allowMarkup)
{
if (MessageLogger.IsEnabled(LogLevel.Information))
if (logger.IsEnabled(LogLevel.Information))
{
// Only attempt to parse/remove markup when the message is expected to contain it.
// Plain text messages may contain characters like '[' that would be rejected by the markup parser.
var logMessage = allowMarkup ? message.RemoveMarkup() : message;
MessageLogger.LogInformation("{Message}", ConsoleHelpers.FormatEmojiPrefix(emoji, MessageConsole, replaceEmoji: true) + logMessage);
logger.LogInformation("{Message}", ConsoleHelpers.FormatEmojiPrefix(emoji, target, replaceEmoji: true) + logMessage);
}

var displayMessage = allowMarkup ? message : message.EscapeMarkup();
Expand All @@ -394,10 +398,10 @@ public void DisplayMessage(KnownEmoji emoji, string message, bool allowMarkup =
grid.Columns[1].Padding = new Padding(0);

grid.AddRow(
new Markup(ConsoleHelpers.FormatEmojiPrefix(emoji, MessageConsole)),
new Markup(ConsoleHelpers.FormatEmojiPrefix(emoji, target)),
new Markup(displayMessage));

MessageConsole.Write(grid);
target.Write(grid);
}

public void DisplayPlainText(string message)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,63 @@ public void DisplayError_WithMarkupCharactersInMessage_DoesNotDoubleEscape()
Assert.DoesNotContain("[[Prod]]", outputString);
}

[Fact]
public void DisplayError_WritesToStderr_RegardlessOfConsoleSetting()
{
var stdoutOutput = new StringBuilder();
var stderrOutput = new StringBuilder();
var stdoutConsole = AnsiConsole.Create(new AnsiConsoleSettings
{
Ansi = AnsiSupport.No,
ColorSystem = ColorSystemSupport.NoColors,
Out = new AnsiConsoleOutput(new StringWriter(stdoutOutput))
});
var stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{
Ansi = AnsiSupport.No,
ColorSystem = ColorSystemSupport.NoColors,
Out = new AnsiConsoleOutput(new StringWriter(stderrOutput))
});

var executionContext = CreateExecutionContext();
var consoleEnvironment = new ConsoleEnvironment(stdoutConsole, stderrConsole);
var interactionService = new ConsoleInteractionService(consoleEnvironment, executionContext, TestHelpers.CreateInteractiveHostEnvironment(), NullLoggerFactory.Instance);

// Console defaults to Standard (stdout), but errors should still go to stderr
interactionService.DisplayError("Something went wrong");

Assert.Empty(stdoutOutput.ToString());
Assert.Contains("Something went wrong", stderrOutput.ToString());
}

[Fact]
public void DisplayMessage_WritesToStdout_WhenConsoleIsStandard()
{
var stdoutOutput = new StringBuilder();
var stderrOutput = new StringBuilder();
var stdoutConsole = AnsiConsole.Create(new AnsiConsoleSettings
{
Ansi = AnsiSupport.No,
ColorSystem = ColorSystemSupport.NoColors,
Out = new AnsiConsoleOutput(new StringWriter(stdoutOutput))
});
var stderrConsole = AnsiConsole.Create(new AnsiConsoleSettings
{
Ansi = AnsiSupport.No,
ColorSystem = ColorSystemSupport.NoColors,
Out = new AnsiConsoleOutput(new StringWriter(stderrOutput))
});

var executionContext = CreateExecutionContext();
var consoleEnvironment = new ConsoleEnvironment(stdoutConsole, stderrConsole);
var interactionService = new ConsoleInteractionService(consoleEnvironment, executionContext, TestHelpers.CreateInteractiveHostEnvironment(), NullLoggerFactory.Instance);

interactionService.DisplayMessage(KnownEmojis.Information, "Status update");

Assert.Contains("Status update", stdoutOutput.ToString());
Assert.Empty(stderrOutput.ToString());
}

[Fact]
public void DisplayMessage_WithUnescapedMarkup_AutoEscapesAndDoesNotThrow()
{
Expand Down
Loading