This is an automated email from the ASF dual-hosted git repository. gnodet pushed a commit to branch ci-issue-11885 in repository https://gitbox.apache.org/repos/asf/maven.git
commit 5e5150c2640784745a3464be4069d8ffcbba8a2c Author: Guillaume Nodet <[email protected]> AuthorDate: Fri Apr 3 22:09:59 2026 +0200 Fix #11885: Disable ANSI colors when stdout is piped or redirected on JDK 22+ Since JDK 22, System.console() always returns non-null even when stdout is piped/redirected. JLine's ForcedSysOut terminal creation bypasses TTY detection, causing ANSI escape codes to appear in piped output on JDK 25+. Use Console.isTerminal() (JDK 22+, via reflection) to explicitly detect pipe/redirection in auto color mode, ensuring colors are disabled when stdout is not a real terminal. Co-Authored-By: Claude Opus 4.6 <[email protected]> --- .../apache/maven/cling/invoker/LookupInvoker.java | 30 +++++++++++++++++----- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/LookupInvoker.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/LookupInvoker.java index 963d8b1706..951300ce45 100644 --- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/LookupInvoker.java +++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/LookupInvoker.java @@ -276,7 +276,7 @@ protected void configureLogging(C context) throws Exception { } else { boolean isBatchMode = !context.options().forceInteractive().orElse(false) && context.options().nonInteractive().orElse(false); - if (isBatchMode || context.options().logFile().isPresent()) { + if (isBatchMode || context.options().logFile().isPresent() || !isTerminal()) { context.coloredOutput = false; } } @@ -297,6 +297,26 @@ protected void configureLogging(C context) throws Exception { } } + /** + * Checks whether the JVM is connected to a real terminal (as opposed to piped/redirected streams). + * On JDK 22+, {@code System.console()} always returns non-null, so we use + * {@code Console.isTerminal()} via reflection to detect pipe/redirection. + * On JDK < 22, a null console indicates that streams are not connected to a terminal. + */ + static boolean isTerminal() { + java.io.Console console = System.console(); + if (console == null) { + return false; + } + try { + return (Boolean) java.io.Console.class.getMethod("isTerminal").invoke(console); + } catch (ReflectiveOperationException e) { + // JDK < 22: Console.isTerminal() does not exist. + // Non-null console means we are connected to a terminal. + return true; + } + } + protected BuildEventListener determineBuildEventListener(C context) { if (context.buildEventListener == null) { context.buildEventListener = doDetermineBuildEventListener(context); @@ -354,12 +374,10 @@ protected void doCreateTerminal(C context, TerminalBuilder builder) { */ protected final void doConfigureWithTerminal(C context, Terminal terminal) { context.terminal = terminal; - // tricky thing: align what JLine3 detected and Maven thinks: + // Align Maven's color setting with what was decided: // if embedded, we default to context.coloredOutput=false unless overridden (see above) - // if not embedded, JLine3 may detect redirection and will create dumb terminal. - // To align Maven with outcomes, we set here color enabled based on these premises. - // Note: Maven3 suffers from similar thing: if you do `mvn3 foo > log.txt`, the output will - // not be not colored (good), but Maven will print out "Message scheme: color". + // if not embedded, configureLogging() detects pipe/redirection via Console.isTerminal() + // and sets context.coloredOutput=false; otherwise JLine terminal type is used as fallback. MessageUtils.setColorEnabled( context.coloredOutput != null ? context.coloredOutput : !Terminal.TYPE_DUMB.equals(terminal.getType()));
