diff --git a/src/dotnet-ef/Program.cs b/src/dotnet-ef/Program.cs index 0ad0cbb64e4..83e678a5021 100644 --- a/src/dotnet-ef/Program.cs +++ b/src/dotnet-ef/Program.cs @@ -9,6 +9,11 @@ internal static class Program { private static int Main(string[] args) { + // Redirect Console.Out to stderr so that help text and diagnostics + // don't pollute stdout. Actual data output uses the original stdout saved by Reporter. + Reporter.SetStdOut(Console.Out); + Console.SetOut(Console.Error); + var app = new CommandLineApplication(throwOnUnexpectedArg: false) { Name = "dotnet ef" }; new RootCommand().Configure(app); diff --git a/src/dotnet-ef/RootCommand.cs b/src/dotnet-ef/RootCommand.cs index 73802a1d391..d019cf8c068 100644 --- a/src/dotnet-ef/RootCommand.cs +++ b/src/dotnet-ef/RootCommand.cs @@ -54,7 +54,7 @@ protected override int Execute(string[] _) { var commands = _args!.TakeWhile(a => a[0] != '-').ToList(); if (_help!.HasValue() - || ShouldHelp(commands)) + || ShouldHelp(commands, _args!)) { return ShowHelp(_help.HasValue(), commands); } @@ -333,9 +333,11 @@ private static string GetVersion() => typeof(RootCommand).Assembly.GetCustomAttribute()! .InformationalVersion; - private static bool ShouldHelp(IReadOnlyList commands) - => commands.Count == 0 - || (commands.Count == 1 + private static bool ShouldHelp(IReadOnlyList commands, IList args) + => args.Count == 0 + || commands.Count == 0 + || (args.Count == 1 + && commands.Count == 1 && (commands[0] == "database" || commands[0] == "dbcontext" || commands[0] == "migrations")); diff --git a/src/ef/AnsiConsole.cs b/src/ef/AnsiConsole.cs index 8403a40f19c..e8f7827a943 100644 --- a/src/ef/AnsiConsole.cs +++ b/src/ef/AnsiConsole.cs @@ -6,6 +6,7 @@ namespace Microsoft.EntityFrameworkCore.Tools; internal static class AnsiConsole { public static readonly AnsiTextWriter Out = new(Console.Out); + public static readonly AnsiTextWriter Error = new(Console.Error); public static void WriteLine(string? text) => Out.WriteLine(text); diff --git a/src/ef/Program.cs b/src/ef/Program.cs index dc5a5deda48..ea7314042c0 100644 --- a/src/ef/Program.cs +++ b/src/ef/Program.cs @@ -16,6 +16,12 @@ private static int Main(string[] args) Console.OutputEncoding = Encoding.UTF8; } + // Redirect Console.Out to stderr so that any user-configured logging providers + // (e.g. ConsoleLogger) don't pollute stdout with diagnostic messages. + // Actual data output uses the original stdout saved by Reporter. + Reporter.SetStdOut(Console.Out); + Console.SetOut(Console.Error); + var app = new CommandLineApplication { Name = "ef" }; new RootCommand().Configure(app); diff --git a/src/ef/Reporter.cs b/src/ef/Reporter.cs index f19e447e299..20efe64bebe 100644 --- a/src/ef/Reporter.cs +++ b/src/ef/Reporter.cs @@ -14,22 +14,31 @@ internal static class Reporter public const string DataPrefix = "data: "; public const string VerbosePrefix = "verbose: "; + private static TextWriter _stdOut = Console.Out; + private static AnsiTextWriter _stdOutAnsi = new(Console.Out); + public static bool IsVerbose { get; set; } public static bool NoColor { get; set; } public static bool PrefixOutput { get; set; } + public static void SetStdOut(TextWriter writer) + { + _stdOut = writer; + _stdOutAnsi = new AnsiTextWriter(writer); + } + [return: NotNullIfNotNull(nameof(value))] public static string? Colorize(string? value, Func colorizeFunc) => NoColor ? value : colorizeFunc(value); public static void WriteError(string? message) - => WriteLine(Prefix(ErrorPrefix, Colorize(message, x => Bold + Red + x + Reset))); + => WriteStdErr(Prefix(ErrorPrefix, Colorize(message, x => Bold + Red + x + Reset))); public static void WriteWarning(string? message) - => WriteLine(Prefix(WarningPrefix, Colorize(message, x => Bold + Yellow + x + Reset))); + => WriteStdErr(Prefix(WarningPrefix, Colorize(message, x => Bold + Yellow + x + Reset))); public static void WriteInformation(string? message) - => WriteLine(Prefix(InfoPrefix, message)); + => WriteStdErr(Prefix(InfoPrefix, message)); public static void WriteData(string? message) => WriteLine(Prefix(DataPrefix, Colorize(message, x => Bold + Gray + x + Reset))); @@ -38,7 +47,7 @@ public static void WriteVerbose(string? message) { if (IsVerbose) { - WriteLine(Prefix(VerbosePrefix, Colorize(message, x => Bold + Black + x + Reset))); + WriteStdErr(Prefix(VerbosePrefix, Colorize(message, x => Bold + Black + x + Reset))); } } @@ -55,11 +64,29 @@ private static void WriteLine(string? value) { if (NoColor) { - Console.WriteLine(value); + _stdOut.WriteLine(value); + } + else + { + _stdOutAnsi.WriteLine(value); + } + } + + private static void WriteStdErr(string? value) + { + if (PrefixOutput) + { + WriteLine(value); + return; + } + + if (NoColor) + { + Console.Error.WriteLine(value); } else { - AnsiConsole.WriteLine(value); + AnsiConsole.Error.WriteLine(value); } } }