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 &lt; 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()));
 

Reply via email to