Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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.CISupport;
import org.apache.maven.api.services.Lookup;
import org.apache.maven.api.services.MessageBuilderFactory;

Expand Down Expand Up @@ -182,11 +183,30 @@ default Optional<OutputStream> stdErr() {
@Nonnull
Optional<List<CoreExtensions>> coreExtensions();

/**
* Returns detected CI system, if any.
*
* @return an {@link Optional} containing the {@link CISupport} collected from CI system. or empty if CI not
* detected.
*/
@Nonnull
Optional<CISupport> ciSupport();

/**
* Returns the options associated with this invocation request.
*
* @return the options object
*/
@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(ciSupport().isPresent() && ciSupport().get().isDebug());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* 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.
*/
public interface CISupport {
/**
* 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 isDebug() {
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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.CISupport;

import static java.util.Objects.requireNonNull;

Expand All @@ -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 CISupport ciSupport;

@SuppressWarnings("ParameterNumber")
public BaseInvokerRequest(
Expand All @@ -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 CISupport ciSupport) {
this.parserRequest = requireNonNull(parserRequest);
this.parsingFailed = parsingFailed;
this.cwd = requireNonNull(cwd);
Expand All @@ -66,6 +69,7 @@ public BaseInvokerRequest(
this.topDirectory = requireNonNull(topDirectory);
this.rootDirectory = rootDirectory;
this.coreExtensions = coreExtensions;
this.ciSupport = ciSupport;
}

@Override
Expand Down Expand Up @@ -117,4 +121,9 @@ public Optional<Path> rootDirectory() {
public Optional<List<CoreExtensions>> coreExtensions() {
return Optional.ofNullable(coreExtensions);
}

@Override
public Optional<CISupport> ciSupport() {
return Optional.ofNullable(ciSupport);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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.CISupport;
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;
Expand Down Expand Up @@ -86,6 +88,9 @@ public LocalContext(ParserRequest parserRequest) {
@Nullable
public List<CoreExtensions> extensions;

@Nullable
public CISupport ciSupport;

public Options options;

public Map<String, String> extraInterpolationSource() {
Expand Down Expand Up @@ -190,6 +195,9 @@ public InvokerRequest parseInvocation(ParserRequest parserRequest) {
parserRequest.logger().error("Error reading core extensions descriptor", e);
}

// CI detection
context.ciSupport = detectCI(context);

// only if not failed so far; otherwise we may have no options to validate
if (!context.parsingFailed) {
validate(context);
Expand Down Expand Up @@ -500,4 +508,19 @@ protected List<CoreExtension> validateCoreExtensionsDescriptorFromFile(
.collect(Collectors.joining(", ")))
.collect(Collectors.joining("; ")));
}

@Nullable
protected CISupport detectCI(LocalContext context) {
List<CISupport> 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(CISupport::name).collect(Collectors.joining(", ")));
}
return detected.get(0);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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.CISupport;
import org.apache.maven.api.cli.logging.AccumulatingLogger;
import org.apache.maven.api.services.BuilderProblem;
import org.apache.maven.api.services.Interpolator;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 (context.invokerRequest.effectiveVerbose()) {
writer.accept(CLIReportingUtils.showVersion(
ProcessHandle.current().info().commandLine().orElse(null), describe(context.terminal)));

Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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.ciSupport().isPresent()) {
CISupport ci = context.invokerRequest.ciSupport().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;
}
}
Expand Down Expand Up @@ -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;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* 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.CISupport;

/**
* Service interface to detect CI system process runs on, if any.
*/
public interface CIDetector {
/**
* Returns non-empty optional with CI information, if CI is detected, empty otherwise.
*/
Optional<CISupport> detectCI();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* 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 org.apache.maven.api.cli.cisupport.CISupport;

/**
* CI detector helper: it uses service discovery (one is always present: {@link GenericCIDetector}) to detect CI, if
* present and returns a list of detections. If result has 2 or more results, it will return the generic result before.
*/
public final class CIDetectorHelper {
private CIDetectorHelper() {}

public static List<CISupport> detectCI() {
List<CIDetector> detectors = ServiceLoader.load(CIDetector.class).stream()
.map(ServiceLoader.Provider::get)
.toList();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not continue with this stream to get List<CIInfo> and get rid of the loop?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed


ArrayList<CISupport> result = new ArrayList<>();
for (CIDetector detector : detectors) {
Optional<CISupport> detected = detector.detectCI();
detected.ifPresent(result::add);
}
if (result.size() > 1) {
// remove generic
result.removeIf(c -> GenericCIDetector.NAME.equals(c.name()));
}
return result;
}
}
Original file line number Diff line number Diff line change
@@ -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.Optional;

import org.apache.maven.api.cli.cisupport.CISupport;

/**
* 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";

@Override
public Optional<CISupport> detectCI() {
String ciEnv = System.getenv("CI");
if (ciEnv != null && !"false".equals(ciEnv)) {
return Optional.of(new CISupport() {
@Override
public String name() {
return NAME;
}

@Override
public String message() {
return "Environment variable CI equals \"true\". Disable detection by removing that variable or by setting it to any other value";
}
});
}
return Optional.empty();
}
}
Loading
Loading