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

cstamas pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven.git


The following commit(s) were added to refs/heads/master by this push:
     new b1466d866c [MNG-8685] Better support for CI systems (#2254)
b1466d866c is described below

commit b1466d866cc4b113a13eb539773edcb398d05db3
Author: Tamas Cservenak <ta...@cservenak.net>
AuthorDate: Sun Apr 13 20:50:07 2025 +0200

    [MNG-8685] Better support for CI systems (#2254)
    
    Improve CI detection of Maven, make possible to propagate some options from 
CI to Maven.
    
    Changes:
    * `InvokerRequest` carries new info: `Optional<CIInfo>`: if present, we run 
on CI, if empty, not.
    * `Parser` (creating `InvokerRequest`) already inspects env and assembles 
invoker request, so make it detect CI as well
    * `CIDetector` is a Java Service (this all happens early, no DI yet) that 
can be extended (like adding CI specific detectors). Core had one 
implementation that became the "generic" that is what Maven 4 had so far.
    * Added "jenkins", "github", "teamcity", "circle" and "travis" support.
    
    ---
    
    https://issues.apache.org/jira/browse/MNG-8685
---
 .../org/apache/maven/api/cli/InvokerRequest.java   | 18 ++++
 .../org/apache/maven/api/cli/cisupport/CIInfo.java | 49 +++++++++++
 .../maven/cling/invoker/BaseInvokerRequest.java    | 11 ++-
 .../org/apache/maven/cling/invoker/BaseParser.java | 23 +++++
 .../apache/maven/cling/invoker/LookupInvoker.java  | 23 ++---
 .../maven/cling/invoker/cisupport/CIDetector.java  | 35 ++++++++
 .../cling/invoker/cisupport/CIDetectorHelper.java  | 50 +++++++++++
 .../cling/invoker/cisupport/CircleCIDetector.java  | 46 ++++++++++
 .../cling/invoker/cisupport/GenericCIDetector.java | 53 ++++++++++++
 .../cling/invoker/cisupport/GithubCIDetector.java  | 52 ++++++++++++
 .../cling/invoker/cisupport/JenkinsCIDetector.java | 46 ++++++++++
 .../invoker/cisupport/TeamcityCIDetector.java      | 46 ++++++++++
 .../cling/invoker/cisupport/TravisCIDetector.java  | 52 ++++++++++++
 .../maven/cling/invoker/mvn/MavenInvoker.java      |  7 +-
 .../cling/invoker/mvn/MavenInvokerRequest.java     |  5 +-
 .../maven/cling/invoker/mvn/MavenParser.java       |  1 +
 .../invoker/mvnenc/EncryptInvokerRequest.java      |  5 +-
 .../maven/cling/invoker/mvnenc/EncryptParser.java  |  1 +
 .../cling/invoker/mvnsh/ShellInvokerRequest.java   |  5 +-
 .../maven/cling/invoker/mvnsh/ShellParser.java     |  1 +
 ...apache.maven.cling.invoker.cisupport.CIDetector |  6 ++
 .../invoker/cisupport/CIDetectorHelperRunner.java  | 34 ++++++++
 .../invoker/cisupport/CIDetectorHelperTest.java    | 97 ++++++++++++++++++++++
 .../MavenITmng4461ArtifactUploadMonitorTest.java   |  2 +-
 .../MavenITmng4829ChecksumFailureWarningTest.java  |  2 +-
 ...avenITmng6240PluginExtensionAetherProvider.java |  4 +-
 .../main/java/org/apache/maven/it/Verifier.java    | 13 +++
 27 files changed, 661 insertions(+), 26 deletions(-)

diff --git 
a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/InvokerRequest.java 
b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/InvokerRequest.java
index e9b2a480ed..64003dc723 100644
--- 
a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/InvokerRequest.java
+++ 
b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/InvokerRequest.java
@@ -28,6 +28,7 @@
 import org.apache.maven.api.annotations.Experimental;
 import org.apache.maven.api.annotations.Immutable;
 import org.apache.maven.api.annotations.Nonnull;
+import org.apache.maven.api.cli.cisupport.CIInfo;
 import org.apache.maven.api.services.Lookup;
 import org.apache.maven.api.services.MessageBuilderFactory;
 
@@ -182,6 +183,15 @@ default Optional<OutputStream> stdErr() {
     @Nonnull
     Optional<List<CoreExtensions>> coreExtensions();
 
+    /**
+     * Returns detected CI system, if any.
+     *
+     * @return an {@link Optional} containing the {@link CIInfo} collected 
from CI system. or empty if CI not
+     * detected.
+     */
+    @Nonnull
+    Optional<CIInfo> ciInfo();
+
     /**
      * Returns the options associated with this invocation request.
      *
@@ -189,4 +199,12 @@ default Optional<OutputStream> stdErr() {
      */
     @Nonnull
     Options options();
+
+    /**
+     * This method returns "verbose" option value derived from multiple 
places: CLI options, but also CI detection,
+     * if applicable.
+     */
+    default boolean effectiveVerbose() {
+        return options().verbose().orElse(ciInfo().isPresent() && 
ciInfo().get().isVerbose());
+    }
 }
diff --git 
a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/cisupport/CIInfo.java
 
b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/cisupport/CIInfo.java
new file mode 100644
index 0000000000..ced54f8373
--- /dev/null
+++ 
b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/cisupport/CIInfo.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.api.cli.cisupport;
+
+import org.apache.maven.api.annotations.Nonnull;
+
+/**
+ * CI support: this class contains gathered information and more from CI that 
Maven process runs on.
+ *
+ * @since 4.0.0
+ */
+public interface CIInfo {
+    /**
+     * Short distinct name of CI system: "GH", "Jenkins", etc.
+     */
+    @Nonnull
+    String name();
+
+    /**
+     * May return a message that will be logged by Maven explaining why it was 
detected (and possibly more).
+     */
+    @Nonnull
+    default String message() {
+        return "";
+    }
+
+    /**
+     * Some CI systems may allow running jobs in "debug" (or some equivalent) 
mode.
+     */
+    default boolean isVerbose() {
+        return false;
+    }
+}
diff --git 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseInvokerRequest.java
 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseInvokerRequest.java
index caef621191..02960ecd54 100644
--- 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseInvokerRequest.java
+++ 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseInvokerRequest.java
@@ -28,6 +28,7 @@
 import org.apache.maven.api.cli.CoreExtensions;
 import org.apache.maven.api.cli.InvokerRequest;
 import org.apache.maven.api.cli.ParserRequest;
+import org.apache.maven.api.cli.cisupport.CIInfo;
 
 import static java.util.Objects.requireNonNull;
 
@@ -42,6 +43,7 @@ public abstract class BaseInvokerRequest implements 
InvokerRequest {
     private final Path topDirectory;
     private final Path rootDirectory;
     private final List<CoreExtensions> coreExtensions;
+    private final CIInfo ciInfo;
 
     @SuppressWarnings("ParameterNumber")
     public BaseInvokerRequest(
@@ -54,7 +56,8 @@ public BaseInvokerRequest(
             @Nonnull Map<String, String> systemProperties,
             @Nonnull Path topDirectory,
             @Nullable Path rootDirectory,
-            @Nullable List<CoreExtensions> coreExtensions) {
+            @Nullable List<CoreExtensions> coreExtensions,
+            @Nullable CIInfo ciInfo) {
         this.parserRequest = requireNonNull(parserRequest);
         this.parsingFailed = parsingFailed;
         this.cwd = requireNonNull(cwd);
@@ -66,6 +69,7 @@ public BaseInvokerRequest(
         this.topDirectory = requireNonNull(topDirectory);
         this.rootDirectory = rootDirectory;
         this.coreExtensions = coreExtensions;
+        this.ciInfo = ciInfo;
     }
 
     @Override
@@ -117,4 +121,9 @@ public Optional<Path> rootDirectory() {
     public Optional<List<CoreExtensions>> coreExtensions() {
         return Optional.ofNullable(coreExtensions);
     }
+
+    @Override
+    public Optional<CIInfo> ciInfo() {
+        return Optional.ofNullable(ciInfo);
+    }
 }
diff --git 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseParser.java 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseParser.java
index 8427203faf..e13f764769 100644
--- 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseParser.java
+++ 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseParser.java
@@ -43,11 +43,13 @@
 import org.apache.maven.api.cli.Options;
 import org.apache.maven.api.cli.Parser;
 import org.apache.maven.api.cli.ParserRequest;
+import org.apache.maven.api.cli.cisupport.CIInfo;
 import org.apache.maven.api.cli.extensions.CoreExtension;
 import org.apache.maven.api.cli.extensions.InputLocation;
 import org.apache.maven.api.cli.extensions.InputSource;
 import org.apache.maven.api.services.Interpolator;
 import org.apache.maven.cling.internal.extension.io.CoreExtensionsStaxReader;
+import org.apache.maven.cling.invoker.cisupport.CIDetectorHelper;
 import org.apache.maven.cling.props.MavenPropertiesLoader;
 import org.apache.maven.cling.utils.CLIReportingUtils;
 import org.apache.maven.properties.internal.EnvironmentUtils;
@@ -86,6 +88,9 @@ public LocalContext(ParserRequest parserRequest) {
         @Nullable
         public List<CoreExtensions> extensions;
 
+        @Nullable
+        public CIInfo ciInfo;
+
         public Options options;
 
         public Map<String, String> extraInterpolationSource() {
@@ -190,6 +195,9 @@ public InvokerRequest parseInvocation(ParserRequest 
parserRequest) {
             parserRequest.logger().error("Error reading core extensions 
descriptor", e);
         }
 
+        // CI detection
+        context.ciInfo = detectCI(context);
+
         // only if not failed so far; otherwise we may have no options to 
validate
         if (!context.parsingFailed) {
             validate(context);
@@ -500,4 +508,19 @@ protected List<CoreExtension> 
validateCoreExtensionsDescriptorFromFile(
                                         .collect(Collectors.joining(", ")))
                         .collect(Collectors.joining("; ")));
     }
+
+    @Nullable
+    protected CIInfo detectCI(LocalContext context) {
+        List<CIInfo> detected = CIDetectorHelper.detectCI();
+        if (detected.isEmpty()) {
+            return null;
+        } else if (detected.size() > 1) {
+            // warn
+            context.parserRequest
+                    .logger()
+                    .warn("Multiple CI systems detected: "
+                            + 
detected.stream().map(CIInfo::name).collect(Collectors.joining(", ")));
+        }
+        return detected.get(0);
+    }
 }
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 a5935320f4..c0b8c50b56 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
@@ -46,6 +46,7 @@
 import org.apache.maven.api.cli.InvokerRequest;
 import org.apache.maven.api.cli.Logger;
 import org.apache.maven.api.cli.Options;
+import org.apache.maven.api.cli.cisupport.CIInfo;
 import org.apache.maven.api.cli.logging.AccumulatingLogger;
 import org.apache.maven.api.services.BuilderProblem;
 import org.apache.maven.api.services.Interpolator;
@@ -278,7 +279,7 @@ protected void configureLogging(C context) throws Exception 
{
         context.slf4jConfiguration = 
Slf4jConfigurationFactory.getConfiguration(context.loggerFactory);
 
         context.loggerLevel = Slf4jConfiguration.Level.INFO;
-        if (mavenOptions.verbose().orElse(false)) {
+        if (context.invokerRequest.effectiveVerbose()) {
             context.loggerLevel = Slf4jConfiguration.Level.DEBUG;
         } else if (mavenOptions.quiet().orElse(false)) {
             context.loggerLevel = Slf4jConfiguration.Level.ERROR;
@@ -465,7 +466,7 @@ protected void showVersion(C context) {
         InvokerRequest invokerRequest = context.invokerRequest;
         if (invokerRequest.options().quiet().orElse(false)) {
             writer.accept(CLIReportingUtils.showVersionMinimal());
-        } else if (invokerRequest.options().verbose().orElse(false)) {
+        } else if (invokerRequest.effectiveVerbose()) {
             writer.accept(CLIReportingUtils.showVersion(
                     ProcessHandle.current().info().commandLine().orElse(null), 
describe(context.terminal)));
 
@@ -493,9 +494,8 @@ protected String describe(Terminal terminal) {
     }
 
     protected void preCommands(C context) throws Exception {
-        Options mavenOptions = context.invokerRequest.options();
-        boolean verbose = mavenOptions.verbose().orElse(false);
-        boolean version = mavenOptions.showVersion().orElse(false);
+        boolean verbose = context.invokerRequest.effectiveVerbose();
+        boolean version = 
context.invokerRequest.options().showVersion().orElse(false);
         if (verbose || version) {
             showVersion(context);
         }
@@ -726,11 +726,11 @@ protected boolean mayDisableInteractiveMode(C context, 
boolean proposedInteracti
             if 
(context.invokerRequest.options().nonInteractive().orElse(false)) {
                 return false;
             } else {
-                boolean runningOnCI = isRunningOnCI(context);
-                if (runningOnCI) {
+                if (context.invokerRequest.ciInfo().isPresent()) {
+                    CIInfo ci = context.invokerRequest.ciInfo().get();
                     context.logger.info(
-                            "Making this build non-interactive, because the 
environment variable CI equals \"true\"."
-                                    + " Disable this detection by removing 
that variable or adding --force-interactive.");
+                            "Making this build non-interactive, because CI 
detected. Disable this detection by adding --force-interactive.");
+                    context.logger.info("Detected CI system: '" + ci.name() + 
"': " + ci.message());
                     return false;
                 }
             }
@@ -935,10 +935,5 @@ protected int calculateDegreeOfConcurrency(String 
threadConfiguration) {
         }
     }
 
-    protected boolean isRunningOnCI(C context) {
-        String ciEnv = 
context.protoSession.getSystemProperties().get("env.CI");
-        return ciEnv != null && !"false".equals(ciEnv);
-    }
-
     protected abstract int execute(C context) throws Exception;
 }
diff --git 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/cisupport/CIDetector.java
 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/cisupport/CIDetector.java
new file mode 100644
index 0000000000..5d056d05d4
--- /dev/null
+++ 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/cisupport/CIDetector.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.cling.invoker.cisupport;
+
+import java.util.Optional;
+
+import org.apache.maven.api.cli.cisupport.CIInfo;
+
+/**
+ * Service interface to detect CI system process runs on, if any.
+ *
+ * @since 4.0.0
+ */
+public interface CIDetector {
+    /**
+     * Returns non-empty optional with CI information, if CI is detected, 
empty otherwise.
+     */
+    Optional<CIInfo> detectCI();
+}
diff --git 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/cisupport/CIDetectorHelper.java
 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/cisupport/CIDetectorHelper.java
new file mode 100644
index 0000000000..6c95755eb8
--- /dev/null
+++ 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/cisupport/CIDetectorHelper.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.cling.invoker.cisupport;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.ServiceLoader;
+import java.util.stream.Collectors;
+
+import org.apache.maven.api.cli.cisupport.CIInfo;
+
+/**
+ * CI detector helper: it uses service discovery to discover {@link 
CIDetector}s. If resulting list has more than
+ * one element, it will remove the {@link GenericCIDetector} result, assuming 
a more specific one is also present.
+ */
+public final class CIDetectorHelper {
+    private CIDetectorHelper() {}
+
+    public static List<CIInfo> detectCI() {
+        ArrayList<CIInfo> result = 
ServiceLoader.load(CIDetector.class).stream()
+                .map(ServiceLoader.Provider::get)
+                .map(CIDetector::detectCI)
+                .filter(Optional::isPresent)
+                .map(Optional::get)
+                .collect(Collectors.toCollection(ArrayList::new));
+
+        if (result.size() > 1) {
+            // remove generic
+            result.removeIf(c -> GenericCIDetector.NAME.equals(c.name()));
+        }
+        return result;
+    }
+}
diff --git 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/cisupport/CircleCIDetector.java
 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/cisupport/CircleCIDetector.java
new file mode 100644
index 0000000000..95c47a382b
--- /dev/null
+++ 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/cisupport/CircleCIDetector.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.cling.invoker.cisupport;
+
+import java.util.Optional;
+
+import org.apache.maven.api.cli.cisupport.CIInfo;
+
+/**
+ * Circle CI support.
+ */
+public class CircleCIDetector implements CIDetector {
+    public static final String NAME = "CircleCI";
+
+    private static final String CIRCLECI = "CIRCLECI";
+
+    @Override
+    public Optional<CIInfo> detectCI() {
+        String ciEnv = System.getenv(CIRCLECI);
+        if ("true".equals(ciEnv)) {
+            return Optional.of(new CIInfo() {
+                @Override
+                public String name() {
+                    return NAME;
+                }
+            });
+        }
+        return Optional.empty();
+    }
+}
diff --git 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/cisupport/GenericCIDetector.java
 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/cisupport/GenericCIDetector.java
new file mode 100644
index 0000000000..4b12406ff2
--- /dev/null
+++ 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/cisupport/GenericCIDetector.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.cling.invoker.cisupport;
+
+import java.util.Optional;
+
+import org.apache.maven.api.cli.cisupport.CIInfo;
+
+/**
+ * Generic CI support. This offers same support as Maven 3 always had. Is also 
special, as code will reject this
+ * detector result IF there are also any other returned via discovered 
services.
+ */
+public class GenericCIDetector implements CIDetector {
+    public static final String NAME = "Generic";
+
+    private static final String CI = "CI";
+
+    @Override
+    public Optional<CIInfo> detectCI() {
+        String ciEnv = System.getenv(CI);
+        if (ciEnv != null && !"false".equals(ciEnv)) {
+            return Optional.of(new CIInfo() {
+                @Override
+                public String name() {
+                    return NAME;
+                }
+
+                @Override
+                public String message() {
+                    return "Environment variable " + CI
+                            + " is set and its value is not \"false\". Disable 
this detection by removing that variable or by setting it to \"false\"";
+                }
+            });
+        }
+        return Optional.empty();
+    }
+}
diff --git 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/cisupport/GithubCIDetector.java
 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/cisupport/GithubCIDetector.java
new file mode 100644
index 0000000000..67ed91a226
--- /dev/null
+++ 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/cisupport/GithubCIDetector.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.cling.invoker.cisupport;
+
+import java.util.Optional;
+
+import org.apache.maven.api.cli.cisupport.CIInfo;
+
+/**
+ * GitHub CI support.
+ */
+public class GithubCIDetector implements CIDetector {
+    public static final String NAME = "GitHub";
+
+    private static final String GITHUB_ACTIONS = "GITHUB_ACTIONS";
+    private static final String RUNNER_DEBUG = "RUNNER_DEBUG";
+
+    @Override
+    public Optional<CIInfo> detectCI() {
+        String ciEnv = System.getenv(GITHUB_ACTIONS);
+        if ("true".equals(ciEnv)) {
+            return Optional.of(new CIInfo() {
+                @Override
+                public String name() {
+                    return NAME;
+                }
+
+                @Override
+                public boolean isVerbose() {
+                    return "1".equals(System.getenv(RUNNER_DEBUG));
+                }
+            });
+        }
+        return Optional.empty();
+    }
+}
diff --git 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/cisupport/JenkinsCIDetector.java
 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/cisupport/JenkinsCIDetector.java
new file mode 100644
index 0000000000..4bb3029ab6
--- /dev/null
+++ 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/cisupport/JenkinsCIDetector.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.cling.invoker.cisupport;
+
+import java.util.Optional;
+
+import org.apache.maven.api.cli.cisupport.CIInfo;
+
+/**
+ * Jenkins CI support.
+ */
+public class JenkinsCIDetector implements CIDetector {
+    public static final String NAME = "Jenkins";
+
+    private static final String WORKSPACE = "WORKSPACE";
+
+    @Override
+    public Optional<CIInfo> detectCI() {
+        String workspace = System.getenv(WORKSPACE);
+        if (workspace != null && !workspace.trim().isEmpty()) {
+            return Optional.of(new CIInfo() {
+                @Override
+                public String name() {
+                    return NAME;
+                }
+            });
+        }
+        return Optional.empty();
+    }
+}
diff --git 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/cisupport/TeamcityCIDetector.java
 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/cisupport/TeamcityCIDetector.java
new file mode 100644
index 0000000000..ed14de34dc
--- /dev/null
+++ 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/cisupport/TeamcityCIDetector.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.cling.invoker.cisupport;
+
+import java.util.Optional;
+
+import org.apache.maven.api.cli.cisupport.CIInfo;
+
+/**
+ * TeamCity CI support.
+ */
+public class TeamcityCIDetector implements CIDetector {
+    public static final String NAME = "TeamCity";
+
+    private static final String TEAMCITY_VERSION = "TEAMCITY_VERSION";
+
+    @Override
+    public Optional<CIInfo> detectCI() {
+        String ciEnv = System.getenv(TEAMCITY_VERSION);
+        if (ciEnv != null && !ciEnv.trim().isEmpty()) {
+            return Optional.of(new CIInfo() {
+                @Override
+                public String name() {
+                    return NAME;
+                }
+            });
+        }
+        return Optional.empty();
+    }
+}
diff --git 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/cisupport/TravisCIDetector.java
 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/cisupport/TravisCIDetector.java
new file mode 100644
index 0000000000..2e43e7e747
--- /dev/null
+++ 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/cisupport/TravisCIDetector.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.cling.invoker.cisupport;
+
+import java.util.Optional;
+
+import org.apache.maven.api.cli.cisupport.CIInfo;
+
+/**
+ * Travis CI support.
+ */
+public class TravisCIDetector implements CIDetector {
+    public static final String NAME = "Travis";
+
+    private static final String TRAVIS = "TRAVIS";
+    private static final String TRAVIS_DEBUG_MODE = "TRAVIS_DEBUG_MODE";
+
+    @Override
+    public Optional<CIInfo> detectCI() {
+        String ciEnv = System.getenv(TRAVIS);
+        if ("true".equals(ciEnv)) {
+            return Optional.of(new CIInfo() {
+                @Override
+                public String name() {
+                    return NAME;
+                }
+
+                @Override
+                public boolean isVerbose() {
+                    return "true".equals(System.getenv(TRAVIS_DEBUG_MODE));
+                }
+            });
+        }
+        return Optional.empty();
+    }
+}
diff --git 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/MavenInvoker.java
 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/MavenInvoker.java
index 1365de51e1..44b435712e 100644
--- 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/MavenInvoker.java
+++ 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/MavenInvoker.java
@@ -362,8 +362,7 @@ protected ExecutionListener 
determineExecutionListener(MavenContext context) {
     protected TransferListener determineTransferListener(MavenContext context, 
boolean noTransferProgress) {
         boolean quiet = context.invokerRequest.options().quiet().orElse(false);
         boolean logFile = 
context.invokerRequest.options().logFile().isPresent();
-        boolean runningOnCI = isRunningOnCI(context);
-        boolean quietCI = runningOnCI
+        boolean quietCI = context.invokerRequest.ciInfo().isPresent()
                 && 
!context.invokerRequest.options().forceInteractive().orElse(false);
 
         TransferListener delegate;
@@ -373,7 +372,7 @@ protected TransferListener 
determineTransferListener(MavenContext context, boole
             SimplexTransferListener simplex = new SimplexTransferListener(new 
ConsoleMavenTransferListener(
                     context.invokerRequest.messageBuilderFactory(),
                     context.terminal.writer(),
-                    context.invokerRequest.options().verbose().orElse(false)));
+                    context.invokerRequest.effectiveVerbose()));
             context.closeables.add(simplex);
             delegate = simplex;
         } else {
@@ -485,7 +484,7 @@ protected int doExecute(MavenContext context, 
MavenExecutionRequest request) thr
                 context.logger.error("To see the full stack trace of the 
errors, re-run Maven with the '"
                         + MessageUtils.builder().strong("-e") + "' switch");
             }
-            if (!context.invokerRequest.options().verbose().orElse(false)) {
+            if (!context.invokerRequest.effectiveVerbose()) {
                 context.logger.error("Re-run Maven using the '"
                         + MessageUtils.builder().strong("-X") + "' switch to 
enable verbose output");
             }
diff --git 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/MavenInvokerRequest.java
 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/MavenInvokerRequest.java
index 2df07aec03..baa03a079d 100644
--- 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/MavenInvokerRequest.java
+++ 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/MavenInvokerRequest.java
@@ -25,6 +25,7 @@
 import org.apache.maven.api.annotations.Nonnull;
 import org.apache.maven.api.cli.CoreExtensions;
 import org.apache.maven.api.cli.ParserRequest;
+import org.apache.maven.api.cli.cisupport.CIInfo;
 import org.apache.maven.api.cli.mvn.MavenOptions;
 import org.apache.maven.cling.invoker.BaseInvokerRequest;
 
@@ -48,6 +49,7 @@ public MavenInvokerRequest(
             Path topDirectory,
             Path rootDirectory,
             List<CoreExtensions> coreExtensions,
+            CIInfo ciInfo,
             MavenOptions options) {
         super(
                 parserRequest,
@@ -59,7 +61,8 @@ public MavenInvokerRequest(
                 systemProperties,
                 topDirectory,
                 rootDirectory,
-                coreExtensions);
+                coreExtensions,
+                ciInfo);
         this.options = requireNonNull(options);
     }
 
diff --git 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/MavenParser.java
 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/MavenParser.java
index fd030ba185..a8238d90ee 100644
--- 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/MavenParser.java
+++ 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/MavenParser.java
@@ -123,6 +123,7 @@ protected MavenInvokerRequest 
getInvokerRequest(LocalContext context) {
                 context.topDirectory,
                 context.rootDirectory,
                 context.extensions,
+                context.ciInfo,
                 (MavenOptions) context.options);
     }
 
diff --git 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/EncryptInvokerRequest.java
 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/EncryptInvokerRequest.java
index f2847b624f..059aecd904 100644
--- 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/EncryptInvokerRequest.java
+++ 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/EncryptInvokerRequest.java
@@ -25,6 +25,7 @@
 import org.apache.maven.api.annotations.Nonnull;
 import org.apache.maven.api.cli.CoreExtensions;
 import org.apache.maven.api.cli.ParserRequest;
+import org.apache.maven.api.cli.cisupport.CIInfo;
 import org.apache.maven.api.cli.mvnenc.EncryptOptions;
 import org.apache.maven.cling.invoker.BaseInvokerRequest;
 
@@ -45,6 +46,7 @@ public EncryptInvokerRequest(
             Path topDirectory,
             Path rootDirectory,
             List<CoreExtensions> coreExtensions,
+            CIInfo ciInfo,
             EncryptOptions options) {
         super(
                 parserRequest,
@@ -56,7 +58,8 @@ public EncryptInvokerRequest(
                 systemProperties,
                 topDirectory,
                 rootDirectory,
-                coreExtensions);
+                coreExtensions,
+                ciInfo);
         this.options = requireNonNull(options);
     }
 
diff --git 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/EncryptParser.java
 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/EncryptParser.java
index 9a1da7ddbd..378f81f383 100644
--- 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/EncryptParser.java
+++ 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/EncryptParser.java
@@ -50,6 +50,7 @@ protected EncryptInvokerRequest 
getInvokerRequest(LocalContext context) {
                 context.topDirectory,
                 context.rootDirectory,
                 context.extensions,
+                context.ciInfo,
                 (EncryptOptions) context.options);
     }
 
diff --git 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnsh/ShellInvokerRequest.java
 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnsh/ShellInvokerRequest.java
index 5a841a9658..0b2fdf31f9 100644
--- 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnsh/ShellInvokerRequest.java
+++ 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnsh/ShellInvokerRequest.java
@@ -25,6 +25,7 @@
 import org.apache.maven.api.annotations.Nonnull;
 import org.apache.maven.api.cli.CoreExtensions;
 import org.apache.maven.api.cli.ParserRequest;
+import org.apache.maven.api.cli.cisupport.CIInfo;
 import org.apache.maven.api.cli.mvnsh.ShellOptions;
 import org.apache.maven.cling.invoker.BaseInvokerRequest;
 
@@ -45,6 +46,7 @@ public ShellInvokerRequest(
             Path topDirectory,
             Path rootDirectory,
             List<CoreExtensions> coreExtensions,
+            CIInfo ciInfo,
             ShellOptions options) {
         super(
                 parserRequest,
@@ -56,7 +58,8 @@ public ShellInvokerRequest(
                 systemProperties,
                 topDirectory,
                 rootDirectory,
-                coreExtensions);
+                coreExtensions,
+                ciInfo);
         this.options = requireNonNull(options);
     }
 
diff --git 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnsh/ShellParser.java
 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnsh/ShellParser.java
index 5c91146f1f..31c87e2f90 100644
--- 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnsh/ShellParser.java
+++ 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnsh/ShellParser.java
@@ -49,6 +49,7 @@ protected ShellInvokerRequest getInvokerRequest(LocalContext 
context) {
                 context.topDirectory,
                 context.rootDirectory,
                 context.extensions,
+                context.ciInfo,
                 (ShellOptions) context.options);
     }
 
diff --git 
a/impl/maven-cli/src/main/resources/META-INF/services/org.apache.maven.cling.invoker.cisupport.CIDetector
 
b/impl/maven-cli/src/main/resources/META-INF/services/org.apache.maven.cling.invoker.cisupport.CIDetector
new file mode 100644
index 0000000000..d7051280e9
--- /dev/null
+++ 
b/impl/maven-cli/src/main/resources/META-INF/services/org.apache.maven.cling.invoker.cisupport.CIDetector
@@ -0,0 +1,6 @@
+org.apache.maven.cling.invoker.cisupport.CircleCIDetector
+org.apache.maven.cling.invoker.cisupport.GenericCIDetector
+org.apache.maven.cling.invoker.cisupport.GithubCIDetector
+org.apache.maven.cling.invoker.cisupport.JenkinsCIDetector
+org.apache.maven.cling.invoker.cisupport.TeamcityCIDetector
+org.apache.maven.cling.invoker.cisupport.TravisCIDetector
diff --git 
a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/cisupport/CIDetectorHelperRunner.java
 
b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/cisupport/CIDetectorHelperRunner.java
new file mode 100644
index 0000000000..2a8cfd1ef0
--- /dev/null
+++ 
b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/cisupport/CIDetectorHelperRunner.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.cling.invoker.cisupport;
+
+import java.util.List;
+
+import org.apache.maven.api.cli.cisupport.CIInfo;
+
+public class CIDetectorHelperRunner {
+    public static void main(String[] args) {
+        List<CIInfo> detect = CIDetectorHelper.detectCI();
+        if (detect.isEmpty()) {
+            System.out.print("NONE;");
+        } else {
+            detect.forEach(d -> System.out.print(d.name() + (d.isVerbose() ? 
"+VERBOSE" : "") + ";"));
+        }
+    }
+}
diff --git 
a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/cisupport/CIDetectorHelperTest.java
 
b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/cisupport/CIDetectorHelperTest.java
new file mode 100644
index 0000000000..8be4e40b7c
--- /dev/null
+++ 
b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/cisupport/CIDetectorHelperTest.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.cling.invoker.cisupport;
+
+import java.nio.file.FileSystems;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.maven.impl.util.Os;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class CIDetectorHelperTest {
+    private static final Set<String> ALL =
+            Set.of("CIRCLECI", "CI", "WORKSPACE", "GITHUB_ACTIONS", 
"TEAMCITY_VERSION", "TRAVIS");
+
+    @Test
+    void none() throws Exception {
+        assertEquals("NONE;", runner(Map.of()));
+    }
+
+    @Test
+    void generic() throws Exception {
+        assertEquals(GenericCIDetector.NAME + ";", runner(Map.of("CI", 
"true")));
+    }
+
+    @Test
+    void jenkins() throws Exception {
+        assertEquals(JenkinsCIDetector.NAME + ";", runner(Map.of("CI", "true", 
"WORKSPACE", "foobar")));
+    }
+
+    @Test
+    void circleci() throws Exception {
+        assertEquals(CircleCIDetector.NAME + ";", runner(Map.of("CIRCLECI", 
"true")));
+    }
+
+    @Test
+    void teamcity() throws Exception {
+        assertEquals(TeamcityCIDetector.NAME + ";", 
runner(Map.of("TEAMCITY_VERSION", "1.2.3")));
+    }
+
+    @Test
+    void github() throws Exception {
+        assertEquals(GithubCIDetector.NAME + ";", runner(Map.of("CI", "true", 
"GITHUB_ACTIONS", "true")));
+    }
+
+    @Test
+    void githubDebug() throws Exception {
+        assertEquals(
+                GithubCIDetector.NAME + "+VERBOSE;",
+                runner(Map.of("CI", "true", "GITHUB_ACTIONS", "true", 
"RUNNER_DEBUG", "1")));
+    }
+
+    @Test
+    void travis() throws Exception {
+        assertEquals(TravisCIDetector.NAME + ";", runner(Map.of("TRAVIS", 
"true")));
+    }
+
+    @Test
+    void travisDebug() throws Exception {
+        assertEquals(
+                TravisCIDetector.NAME + "+VERBOSE;", runner(Map.of("TRAVIS", 
"true", "TRAVIS_DEBUG_MODE", "true")));
+    }
+
+    private static String runner(Map<String, String> add) throws Exception {
+        String separator = FileSystems.getDefault().getSeparator();
+        String classpath = System.getProperty("java.class.path");
+        String path =
+                System.getProperty("java.home") + separator + "bin" + 
separator + (Os.IS_WINDOWS ? "java.exe" : "java");
+        ProcessBuilder processBuilder =
+                new ProcessBuilder(path, "-cp", classpath, 
CIDetectorHelperRunner.class.getName());
+        processBuilder.environment().putAll(add);
+        ALL.stream()
+                .filter(s -> !add.containsKey(s))
+                .forEach(k -> processBuilder.environment().remove(k));
+        Process process = processBuilder.start();
+        process.waitFor();
+        return new String(process.getInputStream().readAllBytes());
+    }
+}
diff --git 
a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4461ArtifactUploadMonitorTest.java
 
b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4461ArtifactUploadMonitorTest.java
index 2917116f3b..12df894678 100644
--- 
a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4461ArtifactUploadMonitorTest.java
+++ 
b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4461ArtifactUploadMonitorTest.java
@@ -45,7 +45,7 @@ public void testit() throws Exception {
         Verifier verifier = newVerifier(testDir.getAbsolutePath());
         verifier.setAutoclean(false);
         verifier.deleteDirectory("target");
-        verifier.setEnvironmentVariable("CI", "false");
+        verifier.removeCIEnvironmentVariables();
         verifier.addCliArgument("validate");
         verifier.execute();
         verifier.verifyErrorFreeLog();
diff --git 
a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4829ChecksumFailureWarningTest.java
 
b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4829ChecksumFailureWarningTest.java
index 414bc60811..f341dee54c 100644
--- 
a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4829ChecksumFailureWarningTest.java
+++ 
b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4829ChecksumFailureWarningTest.java
@@ -51,7 +51,7 @@ public void testit() throws Exception {
         verifier.deleteArtifacts("org.apache.maven.its.mng4829");
         verifier.addCliArgument("-s");
         verifier.addCliArgument("settings.xml");
-        verifier.setEnvironmentVariable("CI", "false");
+        verifier.removeCIEnvironmentVariables();
         verifier.filterFile("settings-template.xml", "settings.xml");
         verifier.addCliArgument("validate");
         verifier.execute();
diff --git 
a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng6240PluginExtensionAetherProvider.java
 
b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng6240PluginExtensionAetherProvider.java
index e413f2151b..b8ef04777d 100644
--- 
a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng6240PluginExtensionAetherProvider.java
+++ 
b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng6240PluginExtensionAetherProvider.java
@@ -54,13 +54,13 @@ public void 
testPluginExtensionDependingOnMavenAetherProvider() throws Exception
         File projectDir = new File(testDir, "project");
 
         Verifier verifier = newVerifier(pluginDir.getAbsolutePath());
-        verifier.setEnvironmentVariable("CI", "false");
+        verifier.removeCIEnvironmentVariables();
         verifier.addCliArgument("install");
         verifier.execute();
         verifier.verifyErrorFreeLog();
 
         verifier = newVerifier(projectDir.getAbsolutePath());
-        verifier.setEnvironmentVariable("CI", "false");
+        verifier.removeCIEnvironmentVariables();
         verifier.addCliArgument("deploy");
         verifier.execute();
         verifier.verifyErrorFreeLog();
diff --git 
a/its/core-it-support/maven-it-helper/src/main/java/org/apache/maven/it/Verifier.java
 
b/its/core-it-support/maven-it-helper/src/main/java/org/apache/maven/it/Verifier.java
index 04be2c6dbf..0b0dd2a1ed 100644
--- 
a/its/core-it-support/maven-it-helper/src/main/java/org/apache/maven/it/Verifier.java
+++ 
b/its/core-it-support/maven-it-helper/src/main/java/org/apache/maven/it/Verifier.java
@@ -296,6 +296,19 @@ public Properties getSystemProperties() {
         return systemProperties;
     }
 
+    /**
+     * This method renders all env variables that are used for CI detection 
(by all known detector) to not trigger.
+     */
+    public void removeCIEnvironmentVariables() {
+        environmentVariables.putAll(Map.of(
+                "CIRCLECI", "",
+                "CI", "false",
+                "GITHUB_ACTIONS", "",
+                "WORKSPACE", "",
+                "TEAMCITY_VERSION", "",
+                "TRAVIS", ""));
+    }
+
     public void setEnvironmentVariable(String key, String value) {
         if (value != null) {
             environmentVariables.put(key, value);

Reply via email to