From 66d54faf0363c16d90534f67cef01ad51553981d Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Wed, 25 Mar 2026 05:45:38 +0000 Subject: [PATCH 1/2] Improve CLI logging and subprocess output handling during redirection - Modify `ConsoleLogger` to duplicate output to `Console.Error` when `Console.IsOutputRedirected` is true. - Update `RunDotnetAsync` in `cli/Program.cs` to capture all subprocess stdout and dump it to `Console.Error` (and `Console.Out` for markdown) if a command fails and standard logging was suppressed. - Proactively mirror subprocess stdout lines matching error/warning patterns to `Console.Error` when output is redirected. - This ensures that build failures and test results are visible in CI logs even when stdout is redirected to files like `$GITHUB_STEP_SUMMARY`. --- cli/Program.cs | 47 ++++++++++++++++++++++---- src/_Hidden_FUnitImpl/ConsoleLogger.cs | 35 ++++++++++++++++--- 2 files changed, 71 insertions(+), 11 deletions(-) diff --git a/cli/Program.cs b/cli/Program.cs index 8726a48..fdd4476 100644 --- a/cli/Program.cs +++ b/cli/Program.cs @@ -455,6 +455,8 @@ async ValueTask RunDotnetAsync( }; var callCounts = new ProcessCallbackCallCounts(); + var stdoutBuffer = new List(); + var stdoutLock = new object(); proc.ErrorDataReceived += (sender, args) => { @@ -465,17 +467,34 @@ async ValueTask RunDotnetAsync( } }; - if (requireStdOutLogging) + proc.OutputDataReceived += (sender, args) => { - proc.OutputDataReceived += (sender, args) => + if (args.Data != null) { - if (args.Data != null) + lock (stdoutLock) + { + stdoutBuffer.Add(args.Data); + } + + if (requireStdOutLogging) { Interlocked.Increment(ref callCounts.Stdout); - Console.WriteLine(Colorize(args.Data)); // DO NOT use ConsoleLogger here! + var colorized = Colorize(args.Data); + Console.WriteLine(colorized); // DO NOT use ConsoleLogger here! + if (Console.IsOutputRedirected) + { + Console.Error.WriteLine(colorized); + } } - }; - } + else if (Console.IsOutputRedirected) + { + if (LogRegex.WarningOrError().IsMatch(args.Data)) + { + Console.Error.WriteLine(Colorize(args.Data)); + } + } + } + }; if (!proc.Start()) { @@ -491,6 +510,22 @@ async ValueTask RunDotnetAsync( await proc.WaitForExitAsync(); + if (proc.ExitCode != 0 && !requireStdOutLogging) + { + lock (stdoutLock) + { + foreach (var line in stdoutBuffer) + { + var colorized = Colorize(line); + Console.Error.WriteLine(colorized); + if (ConsoleLogger.EnableMarkdownOutput) + { + Console.WriteLine(colorized); + } + } + } + } + if (ConsoleLogger.EnableMarkdownOutput) { if (requireDetailsTag) diff --git a/src/_Hidden_FUnitImpl/ConsoleLogger.cs b/src/_Hidden_FUnitImpl/ConsoleLogger.cs index 128fac0..6d22dfd 100644 --- a/src/_Hidden_FUnitImpl/ConsoleLogger.cs +++ b/src/_Hidden_FUnitImpl/ConsoleLogger.cs @@ -49,6 +49,10 @@ private static void Write(object? obj) message = message.Replace("\n", $"\n{new string(' ', SR.IndentationAdjustment)}", StringComparison.Ordinal); Console.Write(message); + if (Console.IsOutputRedirected) + { + Console.Error.Write(message); + } } private static void WriteLine(object? obj = null) @@ -60,6 +64,10 @@ private static void WriteLine(object? obj = null) private static void NewLine() { Console.WriteLine(); + if (Console.IsOutputRedirected) + { + Console.Error.WriteLine(); + } } private static void Color(string? ansiColor, object obj) @@ -91,18 +99,25 @@ private static void Color(string? ansiColor, object obj) // write color if (writeColor) { + var colorTag = string.Empty; if (!EnableMarkdownOutput) { - Console.Write(ansiColor); + colorTag = ansiColor ?? string.Empty; } else { - Console.Write(ansiColor switch + colorTag = ansiColor switch { SR.AnsiColorFailed => SR.MarkdownColorFailed, SR.AnsiColorPassed => SR.MarkdownColorPassed, SR.AnsiColorReset or _ => SR.MarkdownColorReset, - }); + }; + } + + Console.Write(colorTag); + if (Console.IsOutputRedirected) + { + Console.Error.Write(colorTag); } } @@ -115,15 +130,21 @@ private static void Color(string? ansiColor, object obj) // reset color if (writeColor) { + var resetTag = string.Empty; if (!EnableMarkdownOutput) { - Console.Write(SR.AnsiColorReset); + resetTag = SR.AnsiColorReset; } else { - Console.Write(SR.MarkdownColorReset); + resetTag = SR.MarkdownColorReset; } + Console.Write(resetTag); + if (Console.IsOutputRedirected) + { + Console.Error.Write(resetTag); + } } // trailing spaces @@ -149,6 +170,10 @@ public static void LogInfoRaw(object? message = null) lock (sync) { Console.WriteLine(message); + if (Console.IsOutputRedirected) + { + Console.Error.WriteLine(message); + } } } From c1e263773431676684f180a52658a7a1c0c43359 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 26 Mar 2026 02:40:42 +0000 Subject: [PATCH 2/2] Refine CLI redirection logging based on PR feedback - Restore `ConsoleLogger.cs` to its original state. - Update `cli/Program.cs` to simplify logging during redirection: when `Console.IsOutputRedirected` is true, all subprocess output is now immediately logged to both `Console.Out` and `Console.Error`, regardless of other conditions. - Prevent redundant output buffering/dumping when redirection is already handling the logging. --- cli/Program.cs | 11 ++------ src/_Hidden_FUnitImpl/ConsoleLogger.cs | 35 ++++---------------------- 2 files changed, 7 insertions(+), 39 deletions(-) diff --git a/cli/Program.cs b/cli/Program.cs index fdd4476..7b6a6c3 100644 --- a/cli/Program.cs +++ b/cli/Program.cs @@ -476,7 +476,7 @@ async ValueTask RunDotnetAsync( stdoutBuffer.Add(args.Data); } - if (requireStdOutLogging) + if (requireStdOutLogging || Console.IsOutputRedirected) { Interlocked.Increment(ref callCounts.Stdout); var colorized = Colorize(args.Data); @@ -486,13 +486,6 @@ async ValueTask RunDotnetAsync( Console.Error.WriteLine(colorized); } } - else if (Console.IsOutputRedirected) - { - if (LogRegex.WarningOrError().IsMatch(args.Data)) - { - Console.Error.WriteLine(Colorize(args.Data)); - } - } } }; @@ -510,7 +503,7 @@ async ValueTask RunDotnetAsync( await proc.WaitForExitAsync(); - if (proc.ExitCode != 0 && !requireStdOutLogging) + if (proc.ExitCode != 0 && !requireStdOutLogging && !Console.IsOutputRedirected) { lock (stdoutLock) { diff --git a/src/_Hidden_FUnitImpl/ConsoleLogger.cs b/src/_Hidden_FUnitImpl/ConsoleLogger.cs index 6d22dfd..128fac0 100644 --- a/src/_Hidden_FUnitImpl/ConsoleLogger.cs +++ b/src/_Hidden_FUnitImpl/ConsoleLogger.cs @@ -49,10 +49,6 @@ private static void Write(object? obj) message = message.Replace("\n", $"\n{new string(' ', SR.IndentationAdjustment)}", StringComparison.Ordinal); Console.Write(message); - if (Console.IsOutputRedirected) - { - Console.Error.Write(message); - } } private static void WriteLine(object? obj = null) @@ -64,10 +60,6 @@ private static void WriteLine(object? obj = null) private static void NewLine() { Console.WriteLine(); - if (Console.IsOutputRedirected) - { - Console.Error.WriteLine(); - } } private static void Color(string? ansiColor, object obj) @@ -99,25 +91,18 @@ private static void Color(string? ansiColor, object obj) // write color if (writeColor) { - var colorTag = string.Empty; if (!EnableMarkdownOutput) { - colorTag = ansiColor ?? string.Empty; + Console.Write(ansiColor); } else { - colorTag = ansiColor switch + Console.Write(ansiColor switch { SR.AnsiColorFailed => SR.MarkdownColorFailed, SR.AnsiColorPassed => SR.MarkdownColorPassed, SR.AnsiColorReset or _ => SR.MarkdownColorReset, - }; - } - - Console.Write(colorTag); - if (Console.IsOutputRedirected) - { - Console.Error.Write(colorTag); + }); } } @@ -130,21 +115,15 @@ private static void Color(string? ansiColor, object obj) // reset color if (writeColor) { - var resetTag = string.Empty; if (!EnableMarkdownOutput) { - resetTag = SR.AnsiColorReset; + Console.Write(SR.AnsiColorReset); } else { - resetTag = SR.MarkdownColorReset; + Console.Write(SR.MarkdownColorReset); } - Console.Write(resetTag); - if (Console.IsOutputRedirected) - { - Console.Error.Write(resetTag); - } } // trailing spaces @@ -170,10 +149,6 @@ public static void LogInfoRaw(object? message = null) lock (sync) { Console.WriteLine(message); - if (Console.IsOutputRedirected) - { - Console.Error.WriteLine(message); - } } }