Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

No color output when run via Gradle's run task (or Docker) #428

Closed
sschuberth opened this issue Jul 21, 2023 · 7 comments
Closed

No color output when run via Gradle's run task (or Docker) #428

sschuberth opened this issue Jul 21, 2023 · 7 comments

Comments

@sschuberth
Copy link
Contributor

sschuberth commented Jul 21, 2023

This can be reproduced with samples/helpformat:

$ ../../gradlew installDist
$ ./build/install/helpformat/bin/helpformat --help

shows colored help:

image

But

$ ../../gradlew run --args="--help"

does not:

image

I guess this happens because Gradle redirects the output and thus some terminal detection (probably rather in Mordant than in clikt) fails.

A similar thing happens when "dockerizing" apps and (in this case) using the build/install/helpformat/bin/helpformat script as the entry point. Then something like docker run helpformat --help also shows no color.

I'm not sure whether this even can be fixed reliable without making hacky assumptions, but I wanted to bring it up.

@ajalt
Copy link
Owner

ajalt commented Jul 21, 2023

Yes, this is because Mordant is disabling color when output is redirected (see ajalt/mordant#104). Unfortunately, there's no way to know if you're redirected to a file or a pipe that will end up in the screen eventually, so I choose to disable colors to avoid ANSI codes ending up in a file. Most other colored terminal programs do the same.

As mentioned in the linked issue, you can force color with context { terminal = Terminal(AnsiLevel.TRUECOLOR) }

@sschuberth
Copy link
Contributor Author

Yeah, that's also the conclusion people I've asked came up with in this Gradle Slack thread.

Just wondering, with clikt using Mordant now, would it make sense to by default provide --(no-)color CLI optons?

@ajalt
Copy link
Owner

ajalt commented Jul 21, 2023

That's a good idea. I know some popular CLIs have a --no-color option, but I think it doesn't really work if your error messages are colored. If an parse error occurs before --no-color is reached, then we won't be able to disable colors for the error message.

Mordant supports all the popular environment variables (COLORTERM, FORCE_COLOR, and NO_COLOR) for enabling/disabling color, so I'd rather add docs explaining how to use those, since they always work even if there's a parse error.

@sschuberth
Copy link
Contributor Author

I'd rather add docs explaining how to use those, since they always work even if there's a parse error.

Ok. Users who still want --(no-)color CLI options can add them manually then. Thanks, so let me close this.

@sschuberth
Copy link
Contributor Author

Users who still want --(no-)color CLI options can add them manually then.

However, if I try to do

private val color by option(
    help = "Override terminal detection and force the use of true-color output."
).flag()

and

init {
    context {
        if (color) terminal = Terminal(AnsiLevel.TRUECOLOR)
    }
}

I'm running into java.lang.IllegalStateException: Cannot read from option delegate before parsing command line again. And I guess this time #431 is not going to fix it.

Also, I'd like a --color option to even work if also --help is passed. Is there currently a way to make that work?

PS: I'd prefer to not use environment variables to not affect other processes than my own.

@ajalt
Copy link
Owner

ajalt commented Jul 25, 2023

You cannot read from an option in init, since that happens before the command line is parsed. If you really want --color, implement it with an eagerOption eagerOption("--color") { context.terminal.info.ansiLevel = AnsiLevel.TRUECOLOR } (or option("--color", eager=true).flag("--no-color").validate {...} if you want).

But for the same reason i mentioned above, it won't work if you call --help --color, since --help will trigger before --color is reached.

You can set environment variables for a single process, e.g. from the command line FORCE_COLOR=1 ./my-tool or with ProcessBuilder.environment

@sschuberth
Copy link
Contributor Author

sschuberth commented Feb 21, 2024

For reference, I'm now solving this on a Gradle task level:

tasks.named<JavaExec>("run") {
    System.getenv("TERM")?.also {
        val mode = it.substringAfter('-', "16color")
        environment("FORCE_COLOR" to mode)
    }

    System.getenv("COLORTERM")?.also {
        environment("FORCE_COLOR" to it)
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants