diff --git a/maven-embedder/pom.xml b/maven-embedder/pom.xml
index 957d2d79a4..4f7bd0c165 100644
--- a/maven-embedder/pom.xml
+++ b/maven-embedder/pom.xml
@@ -146,6 +146,11 @@ under the License.
commons-cli
commons-cli
+
+ org.junit.jupiter
+ junit-jupiter-params
+ test
+
org.mockito
mockito-core
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 41cb71a829..e643f1a67e 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
@@ -38,6 +38,10 @@ public class CLIManager {
public static final char BATCH_MODE = 'B';
+ public static final String NON_INTERACTIVE = "non-interactive";
+
+ public static final String FORCE_INTERACTIVE = "force-interactive";
+
public static final char SET_USER_PROPERTY = 'D';
/**
@@ -173,7 +177,16 @@ public class CLIManager {
.build());
options.addOption(Option.builder(Character.toString(BATCH_MODE))
.longOpt("batch-mode")
- .desc("Run in non-interactive (batch) mode (disables output color)")
+ .desc("Run in non-interactive mode. Alias for --non-interactive (kept for backwards compatability)")
+ .build());
+ options.addOption(Option.builder()
+ .longOpt(NON_INTERACTIVE)
+ .desc("Run in non-interactive mode. Alias for --batch-mode")
+ .build());
+ options.addOption(Option.builder()
+ .longOpt(FORCE_INTERACTIVE)
+ .desc(
+ "Run in interactive mode. Overrides, if applicable, the CI environment variable and --non-interactive/--batch-mode options")
.build());
options.addOption(Option.builder(SUPPRESS_SNAPSHOT_UPDATES)
.longOpt("no-snapshot-updates")
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 b3f29d57cd..d5700c310e 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
@@ -119,7 +119,10 @@ import org.sonatype.plexus.components.sec.dispatcher.SecUtil;
import org.sonatype.plexus.components.sec.dispatcher.model.SettingsSecurity;
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;
@@ -486,10 +489,10 @@ 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);
@@ -506,7 +509,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);
} else if ("never".equals(styleColor) || "no".equals(styleColor) || "none".equals(styleColor)) {
@@ -514,14 +517,17 @@ public class MavenCli {
} else if (!"auto".equals(styleColor) && !"tty".equals(styleColor) && !"if-tty".equals(styleColor)) {
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)) {
- MessageUtils.setColorEnabled(false);
+ } else {
+ 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)) {
- File logFile = new File(cliRequest.commandLine.getOptionValue(CLIManager.LOG_FILE));
+ if (commandLine.hasOption(CLIManager.LOG_FILE)) {
+ File logFile = new File(commandLine.getOptionValue(CLIManager.LOG_FILE));
logFile = resolveFile(logFile, cliRequest.workingDirectory);
// redirect stdout and stderr to file
@@ -541,8 +547,8 @@ public class MavenCli {
plexusLoggerManager = new Slf4jLoggerManager();
slf4jLogger = slf4jLoggerFactory.getLogger(this.getClass().getName());
- if (cliRequest.commandLine.hasOption(CLIManager.FAIL_ON_SEVERITY)) {
- String logLevelThreshold = cliRequest.commandLine.getOptionValue(CLIManager.FAIL_ON_SEVERITY);
+ if (commandLine.hasOption(CLIManager.FAIL_ON_SEVERITY)) {
+ String logLevelThreshold = commandLine.getOptionValue(CLIManager.FAIL_ON_SEVERITY);
if (slf4jLoggerFactory instanceof MavenSlf4jWrapperFactory) {
LogLevelRecorder logLevelRecorder = new LogLevelRecorder(logLevelThreshold);
@@ -557,7 +563,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.");
}
@@ -1242,7 +1248,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));
@@ -1304,6 +1310,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;
+ }
+
+ boolean runningOnCI = isRunningOnCI(cliRequest.getSystemProperties());
+ if (runningOnCI) {
+ slf4jLogger.info("Making this build non-interactive, 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 static boolean isRunningOnCI(Properties systemProperties) {
+ String ciEnv = systemProperties.getProperty("env.CI");
+ return ciEnv != null && !"false".equals(ciEnv);
+ }
+
private String determineLocalRepositoryPath(final MavenExecutionRequest request) {
String userDefinedLocalRepo = request.getUserProperties().getProperty(MavenCli.LOCAL_REPO_PROPERTY);
if (userDefinedLocalRepo != null) {
@@ -1424,7 +1451,10 @@ public class MavenCli {
final boolean verbose,
final CommandLine commandLine,
final MavenExecutionRequest request) {
- if (quiet || commandLine.hasOption(CLIManager.NO_TRANSFER_PROGRESS)) {
+ boolean runningOnCI = isRunningOnCI(request.getSystemProperties());
+ boolean quietCI = runningOnCI && !commandLine.hasOption(FORCE_INTERACTIVE);
+
+ if (quiet || commandLine.hasOption(CLIManager.NO_TRANSFER_PROGRESS) || quietCI) {
return new QuietMavenTransferListener();
} else if (request.isInteractiveMode() && !commandLine.hasOption(CLIManager.LOG_FILE)) {
//
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 e9e90e7278..7ba238dbc1 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
@@ -26,6 +26,7 @@ import java.nio.file.Path;
import java.nio.file.Paths;
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;
@@ -34,6 +35,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;
@@ -45,9 +49,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;
import static java.util.Arrays.asList;
@@ -333,6 +341,20 @@ 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";
@@ -585,6 +607,68 @@ class MavenCliTest {
assertThat(request.getCommandLine().getArgs(), equalTo(new String[] {"prefix:3.0.0:bar", "validate"}));
}
+ @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 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 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 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();
project.setGroupId(groupId);