You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by ma...@apache.org on 2022/11/10 15:47:32 UTC

[maven] 01/01: [MNG-7338] Automatically activate batch-mode and make output quiet when running in CI.

This is an automated email from the ASF dual-hosted git repository.

martinkanters pushed a commit to branch feature/MNG-7338-automatic-non-interactive-mode-in-ci
in repository https://gitbox.apache.org/repos/asf/maven.git

commit 2a87384e92aed08178aeda45796c72c1ce1d42d6
Author: Martin Kanters <ma...@apache.org>
AuthorDate: Thu Nov 10 16:47:16 2022 +0100

    [MNG-7338] Automatically activate batch-mode and make output quiet when running in CI.
---
 maven-embedder/pom.xml                             |  5 ++
 .../main/java/org/apache/maven/cli/CLIManager.java |  8 ++-
 .../main/java/org/apache/maven/cli/MavenCli.java   | 62 +++++++++++++----
 .../java/org/apache/maven/cli/MavenCliTest.java    | 81 ++++++++++++++++++++++
 4 files changed, 140 insertions(+), 16 deletions(-)

diff --git a/maven-embedder/pom.xml b/maven-embedder/pom.xml
index 1e6c98968..892adb1b7 100644
--- a/maven-embedder/pom.xml
+++ b/maven-embedder/pom.xml
@@ -156,6 +156,11 @@ under the License.
       <groupId>org.apache.commons</groupId>
       <artifactId>commons-lang3</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.junit.jupiter</groupId>
+      <artifactId>junit-jupiter-params</artifactId>
+      <scope>test</scope>
+    </dependency>
     <dependency>
       <groupId>org.mockito</groupId>
       <artifactId>mockito-core</artifactId>
diff --git a/maven-embedder/src/main/java/org/apache/maven/cli/CLIManager.java b/maven-embedder/src/main/java/org/apache/maven/cli/CLIManager.java
index b6005fddf..eaebd5a37 100644
--- a/maven-embedder/src/main/java/org/apache/maven/cli/CLIManager.java
+++ b/maven-embedder/src/main/java/org/apache/maven/cli/CLIManager.java
@@ -40,6 +40,10 @@ public class CLIManager
 
     public static final char BATCH_MODE = 'B';
 
+    public static final String NON_INTERACTIVE = "ni";
+
+    public static final String FORCE_INTERACTIVE = "fi";
+
     public static final char SET_USER_PROPERTY = 'D';
 
     /**
@@ -138,7 +142,9 @@ public class CLIManager
         options.addOption( Option.builder( Character.toString( NON_RECURSIVE ) ).longOpt( "non-recursive" ).desc( "Do not recurse into sub-projects. When used together with -pl, do not recurse into sub-projects of selected aggregators" ).build() );
         options.addOption( Option.builder( Character.toString( UPDATE_SNAPSHOTS ) ).longOpt( "update-snapshots" ).desc( "Forces a check for missing releases and updated snapshots on remote repositories" ).build() );
         options.addOption( Option.builder( Character.toString( ACTIVATE_PROFILES ) ).longOpt( "activate-profiles" ).desc( "Comma-delimited list of profiles to activate. Prefixing a profile with ! excludes it, and ? marks it as optional" ).hasArg().build() );
-        options.addOption( Option.builder( Character.toString( BATCH_MODE ) ).longOpt( "batch-mode" ).desc( "Run in non-interactive (batch) mode (disables output color)" ).build() );
+        options.addOption( Option.builder( Character.toString( BATCH_MODE ) ).longOpt( "batch-mode" ).desc( "Run in non-interactive (batch) mode (disables output color) - alias for --non-interactive (kept for backwards compatability)" ).build() );
+        options.addOption( Option.builder( NON_INTERACTIVE ).longOpt( "non-interactive" ).desc( "Run in non-interactive (batch) mode (disables output color) - alias for --batch-mode" ).build() );
+        options.addOption( Option.builder( FORCE_INTERACTIVE ).longOpt( "force-interactive" ).desc( "Run in interactive mode - even when the environment variable CI is set to true and --non-interactive or --batch-mode are set" ).build() );
         options.addOption( Option.builder( SUPPRESS_SNAPSHOT_UPDATES ).longOpt( "no-snapshot-updates" ).desc( "Suppress SNAPSHOT updates" ).build() );
         options.addOption( Option.builder( Character.toString( CHECKSUM_FAILURE_POLICY ) ).longOpt( "strict-checksums" ).desc( "Fail the build if checksums don't match" ).build() );
         options.addOption( Option.builder( Character.toString( CHECKSUM_WARNING_POLICY ) ).longOpt( "lax-checksums" ).desc( "Warn if checksums don't match" ).build() );
diff --git a/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java b/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java
index ebfb0830b..2c9ae0ce3 100644
--- a/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java
+++ b/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java
@@ -121,7 +121,10 @@ import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 import static java.util.Comparator.comparing;
+import static org.apache.maven.cli.CLIManager.BATCH_MODE;
 import static org.apache.maven.cli.CLIManager.COLOR;
+import static org.apache.maven.cli.CLIManager.FORCE_INTERACTIVE;
+import static org.apache.maven.cli.CLIManager.NON_INTERACTIVE;
 import static org.apache.maven.cli.ResolveFile.resolveFile;
 import static org.apache.maven.shared.utils.logging.MessageUtils.buffer;
 
@@ -504,10 +507,11 @@ public class MavenCli
     void logging( CliRequest cliRequest )
     {
         // LOG LEVEL
-        cliRequest.verbose = cliRequest.commandLine.hasOption( CLIManager.VERBOSE )
-                             || cliRequest.commandLine.hasOption( CLIManager.DEBUG );
-        cliRequest.quiet = !cliRequest.verbose && cliRequest.commandLine.hasOption( CLIManager.QUIET );
-        cliRequest.showErrors = cliRequest.verbose || cliRequest.commandLine.hasOption( CLIManager.ERRORS );
+        CommandLine commandLine = cliRequest.commandLine;
+        cliRequest.verbose = commandLine.hasOption( CLIManager.VERBOSE )
+                             || commandLine.hasOption( CLIManager.DEBUG );
+        cliRequest.quiet = !cliRequest.verbose && commandLine.hasOption( CLIManager.QUIET );
+        cliRequest.showErrors = cliRequest.verbose || commandLine.hasOption( CLIManager.ERRORS );
 
         slf4jLoggerFactory = LoggerFactory.getILoggerFactory();
         Slf4jConfiguration slf4jConfiguration = Slf4jConfigurationFactory.getConfiguration( slf4jLoggerFactory );
@@ -527,7 +531,7 @@ public class MavenCli
 
         // LOG COLOR
         String styleColor = cliRequest.getUserProperties().getProperty( STYLE_COLOR_PROPERTY, "auto" );
-        styleColor = cliRequest.commandLine.getOptionValue( COLOR, styleColor );
+        styleColor = commandLine.getOptionValue( COLOR, styleColor );
         if ( "always".equals( styleColor ) || "yes".equals( styleColor ) || "force".equals( styleColor ) )
         {
             MessageUtils.setColorEnabled( true );
@@ -541,16 +545,20 @@ public class MavenCli
             throw new IllegalArgumentException( "Invalid color configuration value '" + styleColor
                 + "'. Supported are 'auto', 'always', 'never'." );
         }
-        else if ( cliRequest.commandLine.hasOption( CLIManager.BATCH_MODE )
-            || cliRequest.commandLine.hasOption( CLIManager.LOG_FILE ) )
+        else
         {
-            MessageUtils.setColorEnabled( false );
+            boolean isBatchMode = !commandLine.hasOption( FORCE_INTERACTIVE )
+                    && ( commandLine.hasOption( BATCH_MODE ) || commandLine.hasOption( NON_INTERACTIVE ) );
+            if ( isBatchMode || commandLine.hasOption( CLIManager.LOG_FILE ) )
+            {
+                MessageUtils.setColorEnabled( false );
+            }
         }
 
         // LOG STREAMS
-        if ( cliRequest.commandLine.hasOption( CLIManager.LOG_FILE ) )
+        if ( commandLine.hasOption( CLIManager.LOG_FILE ) )
         {
-            File logFile = new File( cliRequest.commandLine.getOptionValue( CLIManager.LOG_FILE ) );
+            File logFile = new File( commandLine.getOptionValue( CLIManager.LOG_FILE ) );
             logFile = resolveFile( logFile, cliRequest.workingDirectory );
 
             // redirect stdout and stderr to file
@@ -573,9 +581,9 @@ public class MavenCli
         plexusLoggerManager = new Slf4jLoggerManager();
         slf4jLogger = slf4jLoggerFactory.getLogger( this.getClass().getName() );
 
-        if ( cliRequest.commandLine.hasOption( CLIManager.FAIL_ON_SEVERITY ) )
+        if ( commandLine.hasOption( CLIManager.FAIL_ON_SEVERITY ) )
         {
-            String logLevelThreshold = cliRequest.commandLine.getOptionValue( CLIManager.FAIL_ON_SEVERITY );
+            String logLevelThreshold = commandLine.getOptionValue( CLIManager.FAIL_ON_SEVERITY );
 
             if ( slf4jLoggerFactory instanceof MavenSlf4jWrapperFactory )
             {
@@ -591,7 +599,7 @@ public class MavenCli
             }
         }
 
-        if ( cliRequest.commandLine.hasOption( CLIManager.DEBUG ) )
+        if ( commandLine.hasOption( CLIManager.DEBUG ) )
         {
             slf4jLogger.warn( "The option '--debug' is deprecated and may be repurposed as Java debug"
                     + " in a future version. Use -X/--verbose instead." );
@@ -1374,7 +1382,7 @@ public class MavenCli
         request.setShowErrors( cliRequest.showErrors ); // default: false
         File baseDirectory = new File( workingDirectory, "" ).getAbsoluteFile();
 
-        disableOnPresentOption( commandLine, CLIManager.BATCH_MODE, request::setInteractiveMode );
+        disableInteractiveModeIfNeeded( cliRequest, request );
         enableOnPresentOption( commandLine, CLIManager.SUPPRESS_SNAPSHOT_UPDATES, request::setNoSnapshotUpdates );
         request.setGoals( commandLine.getArgList() );
         request.setReactorFailureBehavior( determineReactorFailureBehaviour ( commandLine ) );
@@ -1438,6 +1446,27 @@ public class MavenCli
         return request;
     }
 
+    private void disableInteractiveModeIfNeeded( final CliRequest cliRequest, final MavenExecutionRequest request )
+    {
+        CommandLine commandLine = cliRequest.getCommandLine();
+        if ( commandLine.hasOption( FORCE_INTERACTIVE ) )
+        {
+            return;
+        }
+
+        String ciEnv = cliRequest.getSystemProperties().getProperty( "env.CI" );
+        if ( "true".equals( ciEnv ) )
+        {
+            slf4jLogger.info( "Detected a CI build, because the environment variable CI equals \"true\". "
+                + "Disable this detection by removing that variable or adding --force-interactive." );
+            request.setInteractiveMode( false );
+        }
+        else if ( commandLine.hasOption( BATCH_MODE ) || commandLine.hasOption( NON_INTERACTIVE ) )
+        {
+            request.setInteractiveMode( false );
+        }
+    }
+
     private String determineLocalRepositoryPath( final MavenExecutionRequest request )
     {
         String userDefinedLocalRepo = request.getUserProperties().getProperty( MavenCli.LOCAL_REPO_PROPERTY );
@@ -1593,7 +1622,10 @@ public class MavenCli
                                                         final CommandLine commandLine,
                                                         final MavenExecutionRequest request )
     {
-        if ( quiet || commandLine.hasOption( CLIManager.NO_TRANSFER_PROGRESS ) )
+        String ciEnv = request.getSystemProperties().getProperty( "env.CI" );
+        boolean quietCI = "true".equals( ciEnv ) && !commandLine.hasOption( FORCE_INTERACTIVE );
+
+        if ( quiet || commandLine.hasOption( CLIManager.NO_TRANSFER_PROGRESS ) || quietCI )
         {
             return new QuietMavenTransferListener();
         }
diff --git a/maven-embedder/src/test/java/org/apache/maven/cli/MavenCliTest.java b/maven-embedder/src/test/java/org/apache/maven/cli/MavenCliTest.java
index c4fb01a91..54637c3fa 100644
--- a/maven-embedder/src/test/java/org/apache/maven/cli/MavenCliTest.java
+++ b/maven-embedder/src/test/java/org/apache/maven/cli/MavenCliTest.java
@@ -43,6 +43,7 @@ import java.io.PrintStream;
 import java.nio.charset.StandardCharsets;
 import java.util.Collections;
 import java.util.List;
+import java.util.stream.Stream;
 
 import org.apache.commons.cli.CommandLine;
 import org.apache.commons.cli.CommandLineParser;
@@ -51,6 +52,9 @@ import org.apache.commons.cli.Option;
 import org.apache.commons.cli.Options;
 import org.apache.commons.cli.ParseException;
 import org.apache.maven.Maven;
+import org.apache.maven.cli.transfer.ConsoleMavenTransferListener;
+import org.apache.maven.cli.transfer.QuietMavenTransferListener;
+import org.apache.maven.cli.transfer.Slf4jMavenTransferListener;
 import org.apache.maven.eventspy.internal.EventSpyDispatcher;
 import org.apache.maven.execution.MavenExecutionRequest;
 import org.apache.maven.execution.ProfileActivation;
@@ -61,9 +65,13 @@ import org.apache.maven.toolchain.building.ToolchainsBuildingRequest;
 import org.apache.maven.toolchain.building.ToolchainsBuildingResult;
 import org.codehaus.plexus.DefaultPlexusContainer;
 import org.codehaus.plexus.PlexusContainer;
+import org.eclipse.aether.transfer.TransferListener;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
 import org.mockito.InOrder;
 
 public class MavenCliTest
@@ -359,6 +367,20 @@ public class MavenCliTest
         cli.logging( request );
         assertFalse( MessageUtils.isColorEnabled() );
 
+        MessageUtils.setColorEnabled( true );
+        request = new CliRequest( new String[] { "--non-interactive" }, null );
+        cli.cli( request );
+        cli.properties( request );
+        cli.logging( request );
+        assertFalse( MessageUtils.isColorEnabled() );
+
+        MessageUtils.setColorEnabled( true );
+        request = new CliRequest( new String[] { "--force-interactive", "--non-interactive" }, null );
+        cli.cli( request );
+        cli.properties( request );
+        cli.logging( request );
+        assertTrue( MessageUtils.isColorEnabled() );
+
         MessageUtils.setColorEnabled( true );
         request = new CliRequest( new String[] { "-l", "target/temp/mvn.log" }, null );
         request.workingDirectory = "target/temp";
@@ -588,6 +610,65 @@ public class MavenCliTest
         assertThat( request.getUserProperties().getProperty( "x" ), is( "false" ) );
     }
 
+    @ParameterizedTest
+    @MethodSource("activateBatchModeArguments")
+    public void activateBatchMode(boolean ciEnv, String[] cliArgs, boolean isBatchMode )
+            throws Exception
+    {
+        CliRequest request = new CliRequest( cliArgs, null );
+        if (ciEnv) request.getSystemProperties().put( "env.CI", "true" );
+        cli.cli( request );
+
+        boolean batchMode = !cli.populateRequest(request).isInteractiveMode();
+
+        assertThat( batchMode, is( isBatchMode ) );
+    }
+
+    public static Stream<Arguments> activateBatchModeArguments() {
+        return Stream.of(
+                Arguments.of(false, new String[]{ }, false),
+                Arguments.of(true, new String[]{ }, true),
+                Arguments.of(true, new String[]{ "--force-interactive" }, false),
+                Arguments.of(true, new String[]{ "--force-interactive", "--non-interactive" }, false),
+                Arguments.of(true, new String[]{ "--force-interactive", "--batch-mode" }, false),
+                Arguments.of(true, new String[]{ "--force-interactive", "--non-interactive", "--batch-mode" }, false),
+                Arguments.of(false, new String[]{ "--non-interactive" }, true),
+                Arguments.of(false, new String[]{ "--batch-mode" }, true),
+                Arguments.of(false, new String[]{ "--non-interactive", "--batch-mode" }, true)
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource("calculateTransferListenerArguments")
+    public void calculateTransferListener( boolean ciEnv, String[] cliArgs, Class<TransferListener> expectedSubClass)
+            throws Exception
+    {
+        CliRequest request = new CliRequest( cliArgs, null );
+        if (ciEnv) request.getSystemProperties().put( "env.CI", "true" );
+        cli.cli( request );
+        cli.logging( request );
+
+        TransferListener transferListener = cli.populateRequest(request).getTransferListener();
+
+        assertThat( transferListener.getClass(), is( expectedSubClass ) );
+    }
+
+    public static Stream<Arguments> calculateTransferListenerArguments() {
+        return Stream.of(
+                Arguments.of(false, new String[]{ }, ConsoleMavenTransferListener.class),
+                Arguments.of(true, new String[]{ }, QuietMavenTransferListener.class),
+                Arguments.of(false, new String[]{ "-ntp" }, QuietMavenTransferListener.class),
+                Arguments.of(false, new String[]{ "--quiet" }, QuietMavenTransferListener.class),
+                Arguments.of(true, new String[]{ "--force-interactive" }, ConsoleMavenTransferListener.class),
+                Arguments.of(true, new String[]{ "--force-interactive", "--non-interactive" }, ConsoleMavenTransferListener.class),
+                Arguments.of(true, new String[]{ "--force-interactive", "--batch-mode" }, ConsoleMavenTransferListener.class),
+                Arguments.of(true, new String[]{ "--force-interactive", "--non-interactive", "--batch-mode" }, ConsoleMavenTransferListener.class),
+                Arguments.of(false, new String[]{ "--non-interactive" }, Slf4jMavenTransferListener.class ),
+                Arguments.of(false, new String[]{ "--batch-mode" }, Slf4jMavenTransferListener.class ),
+                Arguments.of(false, new String[]{ "--non-interactive", "--batch-mode" }, Slf4jMavenTransferListener.class )
+        );
+    }
+
     private MavenProject createMavenProject( String groupId, String artifactId )
     {
         MavenProject project = new MavenProject();