dotnet / command-line-api

Command line parsing, invocation, and rendering of terminal output.
https://github.com/dotnet/command-line-api/wiki
MIT License
3.41k stars 382 forks source link

Support for FORCE_COLOR env var #1710

Open xt0rted opened 2 years ago

xt0rted commented 2 years ago

Libraries like node's chalk and picocolors, as well as some popular python projects based on recent discussions, support an env var called FORCE_COLOR (and the inverse NO_COLOR) to let the user force color output. This is useful for things like CI environments where they support colors but due to running in child processes color support gets disabled (example of this enabled with jest test output in github actions).

The way this works is when checking to see if the environment supports colors they look for a FORCE_COLOR env var and if it's set then color support is enabled regardless of what the environment detection set, or color support is disabled in the case of NO_COLOR. Since those two libraries are what most node tools/libraries use a lot of things light up when you enable this which greatly improves looking over build logs for instance.

I tested adding this setting to one of my projects but I'm now wondering if this is something you'd be interested in supporting in the library directly so all tools using this will benefit from it?

The project I'm testing with is a dotnet equivalent of npm run ... so it shows the console output of other tools and would really benefit from .net console libraries supporting this. I'd also just like to see better build log output from dotnet.

Examples of my test output can be seen in the build logs here:

kasperk81 commented 2 years ago

NO_COLOR

net6.0+ respects NO_COLOR. it is implemented as described here: https://no-color.org/

"when present (regardless of its value), prevents the addition of ANSI color. "

FORCE_COLOR

FORCE_COLOR (the opposite) has no RFC or significant adaptation compared to NO_COLOR. closest thing to documentation is https://nodejs.org/api/cli.html#force_color1-2-3 which is involved: value 1 means 16 color, 2 implies 256 colors 3 => 16 mil colors and "any other value disables the color if it was enabled".. not simple as NO_COLOR spec, is it?

net6.0+ however supports another variable: DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION. to turn it on, set value to 1 or true (case insensitive). unlike FORCE_COLOR, its "any other value" will not disable the color if it were previously enabled.

https://github.com/dotnet/runtime/commit/0d848f68b46cd34bca7f64b11b79057f4b7bfad5 https://github.com/dotnet/runtime/issues/33980

xt0rted commented 2 years ago

Maybe I'm not understanding how this works, but if you're outputting colors based on this check then doesn't the DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION env var not matter because colors could be disabled before even making it to the runtime? https://github.com/dotnet/command-line-api/blob/18069a61c3462ca37e52d61b6362d7dc97db47ab/src/System.CommandLine.Rendering/ConsoleFormatInfo.cs#L83-L111

xt0rted commented 2 years ago

Another test I tried was on GitHub Actions running dotnet build with DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION set to 1. The Build succeeded text should be green, but it's not in the build output. The same is true for running dotnet test which should have colored output but doesn't. This was with 6.0.201 of the sdk.

The only way I've been able to get any colors in my build logs is by implementing the FORCE_COLOR setting in my application, but then it's only my application that outputs color, anything being called by it still outputs black and white to the console/build log.

kasperk81 commented 2 years ago

Program.cs

Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Hello, World!");

with redirected stdout dotnet run > somefile the contents of somefile are Hello World! (without color codes). with DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION=1 dotnet run > somefile it has ^[[39;49m^[[91mHello, World!

if dontet test or command-line-api are filtering its effect, then they shouldn't do that and let user take advantage of DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION and NO_COLOR supported by core runtime library.

xt0rted commented 2 years ago

If I run this in WSL I get color codes written to the output file.

DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION=1 dotnet build > output.txt

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:02.33

If I run this in WSL I do not get color output.

NO_COLOR=1 dotnet build

If I run this in PowerShell on Windows 11 I do not get color codes written to the output file.

$env:DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION = '1'; dotnet build > output.txt
Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:01.67

If I run this in PowerShell on Windows 11 I still get color output.

$env:NO_COLOR= '1'; dotnet build

All of this is just testing the sdk cli, but I've been seeing similar results with my own project using this library because of the check linked in my prior comment.

kasperk81 commented 2 years ago
DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION=1 dotnet build > output.txt 2>&1

these options are of course specific to unix

xt0rted commented 2 years ago

There's a couple issues related to this in the runtime and actions repos right now. What it comes down to is on Unix systems you need to set DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION AND TERM: xterm on GitHub Actions to get color output. Due to how the dotnet console apis are implemented it doesn't work on Windows, only macOS and Ubuntu.

If you use the helpers in this library, specifically SystemConsole and Ansi then you bypass the runtime limitation and can get color output both locally and on GitHub Actions. This explains why I was able to force color output in my own tool, but not get it from the dotnet cli.

This is the runtime issue asking for Windows support https://github.com/dotnet/runtime/issues/68340. As I mentioned though, I'm pretty sure this check needs updating because you should be able to force color output on Windows using this method. I'm doing it already but it really should be built-in.

https://github.com/dotnet/command-line-api/blob/dd44dd05a20bf6af02615d8363948a630e821d00/src/System.CommandLine.Rendering/ConsoleFormatInfo.cs#L96-L111