Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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 @@ -213,6 +213,39 @@ public interface MavenOptions extends Options {
*/
Optional<String> atFile();

/**
* Returns the list of lifecycle phases to skip (mojos bound to these phases will not be executed).
*
* <p>This provides a plugin-agnostic alternative to per-plugin skip properties such as
* {@code -DskipTests}. All mojos bound to the listed phases are suppressed, regardless of
* which plugin they belong to. The phases themselves remain in the lifecycle DAG — only their
* mojo executions are inhibited.</p>
*
* <p>Example: {@code --skip-phases=test,integration-test} suppresses all test execution
* while still running compile, package, and verify.</p>
*
* @return an {@link Optional} containing the list of phase names to skip, or empty if not specified
* @since 4.1.0
*/
@Nonnull
Optional<List<String>> skippedPhases();

/**
* Returns whether to skip all test-related phases ({@code test} and {@code integration-test}).
*
* <p>This is a convenient shorthand for {@code --skip-phases=test,integration-test}. It suppresses
* all mojos bound to the {@code test} and {@code integration-test} lifecycle phases, regardless of
* which plugin they belong to, while still running {@code verify} checks (e.g. checkstyle, spotbugs).</p>
*
* <p>Unlike {@code -DskipTests} which is maven-surefire-plugin-specific, this option works with
* any test plugin.</p>
*
* @return an {@link Optional} containing {@code true} if tests should be skipped, or empty if not specified
* @since 4.1.0
*/
@Nonnull
Optional<Boolean> skipTests();

/**
* Returns the list of goals and phases to execute.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,24 @@ public Optional<String> atFile() {
return Optional.empty();
}

@Override
public Optional<List<String>> skippedPhases() {
if (commandLine.hasOption(CLIManager.SKIP_PHASES)) {
return Optional.of(Arrays.stream(commandLine.getOptionValues(CLIManager.SKIP_PHASES))
.map(String::strip)
.toList());
}
return Optional.empty();
}

@Override
public Optional<Boolean> skipTests() {
if (commandLine.hasOption(CLIManager.SKIP_TESTS)) {
return Optional.of(true);
}
return Optional.empty();
}

@Override
public Optional<List<String>> goals() {
if (!commandLine.getArgList().isEmpty()) {
Expand Down Expand Up @@ -252,6 +270,8 @@ protected static class CLIManager extends CommonsCliOptions.CLIManager {
public static final String STRICT_ARTIFACT_DESCRIPTOR_POLICY = "sadp";
public static final String IGNORE_TRANSITIVE_REPOSITORIES = "itr";
public static final String AT_FILE = "af";
public static final String SKIP_PHASES = "sp";
public static final String SKIP_TESTS = "st";

@Override
protected void prepareOptions(org.apache.commons.cli.Options options) {
Expand Down Expand Up @@ -360,6 +380,19 @@ protected void prepareOptions(org.apache.commons.cli.Options options) {
.desc(
"If set, Maven will load command line options from the specified file and merge with CLI specified ones.")
.get());
options.addOption(Option.builder(SKIP_PHASES)
.longOpt("skip-phases")
.hasArgs()
.valueSeparator(',')
.desc("Comma-separated list of lifecycle phases whose mojo executions should be skipped."
+ " The phases remain in the DAG; only their bound mojos are suppressed."
+ " Example: --skip-phases=test,integration-test")
.get());
options.addOption(Option.builder(SKIP_TESTS)
.longOpt("skip-tests")
.desc("Skip test and integration-test phases."
+ " Shorthand for --skip-phases=test,integration-test.")
.get());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,16 @@ public Optional<List<String>> goals() {
return collectListIfPresentOrEmpty(MavenOptions::goals);
}

@Override
public Optional<List<String>> skippedPhases() {
return collectListIfPresentOrEmpty(MavenOptions::skippedPhases);
}

@Override
public Optional<Boolean> skipTests() {
return returnFirstPresentOrEmpty(MavenOptions::skipTests);
}

@Override
public MavenOptions interpolate(UnaryOperator<String> callback) {
ArrayList<MavenOptions> interpolatedOptions = new ArrayList<>(options.size());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,17 @@ protected void populateRequest(MavenContext context, Lookup lookup, MavenExecuti

request.setNoSnapshotUpdates(context.options().suppressSnapshotUpdates().orElse(false));
request.setGoals(context.options().goals().orElse(List.of()));
request.setSkippedPhases(context.options().skippedPhases().orElse(List.of()));
if (context.options().skipTests().orElse(false)) {
List<String> phases = new ArrayList<>(request.getSkippedPhases());
if (!phases.contains("test")) {
phases.add("test");
}
if (!phases.contains("integration-test")) {
phases.add("integration-test");
}
request.setSkippedPhases(phases);
Comment thread
gnodet marked this conversation as resolved.
}
request.setReactorFailureBehavior(determineReactorFailureBehaviour(context));
request.setRecursive(!context.options().nonRecursive().orElse(!request.isRecursive()));
request.setOffline(context.options().offline().orElse(request.isOffline()));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*
* 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.mvn;

import java.util.List;
import java.util.Optional;

import org.apache.commons.cli.ParseException;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;

/**
* Unit tests for {@link CommonsCliMavenOptions} covering the {@code --skip-phases} and
* {@code --skip-tests} options introduced in MNG-13230.
*/
class CommonsCliMavenOptionsTest {

// -------------------------------------------------------------------------
// --skip-phases
// -------------------------------------------------------------------------

@Test
void skippedPhasesNotPresentByDefault() throws ParseException {
CommonsCliMavenOptions options = CommonsCliMavenOptions.parse("test", new String[] {"verify"});
assertEquals(Optional.empty(), options.skippedPhases());
}

@Test
void skippedPhasesSingleValue() throws ParseException {
// Use = form so Commons CLI does not consume the goal "verify" as a phase value.
CommonsCliMavenOptions options =
CommonsCliMavenOptions.parse("test", new String[] {"--skip-phases=test", "verify"});
assertEquals(Optional.of(List.of("test")), options.skippedPhases());
}

@Test
void skippedPhasesMultipleValuesCommaSeparated() throws ParseException {
CommonsCliMavenOptions options =
CommonsCliMavenOptions.parse("test", new String[] {"--skip-phases=test,integration-test", "verify"});
assertEquals(Optional.of(List.of("test", "integration-test")), options.skippedPhases());
}

/**
* Whitespace around comma-separated values must be stripped so that
* {@code --skip-phases "test, integration-test"} works like
* {@code --skip-phases "test,integration-test"}.
* <p>
* Commons CLI splits on the {@code valueSeparator(',')} and returns individual tokens;
* the implementation must strip leading/trailing whitespace from each token.
*/
@Test
void skippedPhasesStripsWhitespace() throws ParseException {
CommonsCliMavenOptions options =
CommonsCliMavenOptions.parse("test", new String[] {"--skip-phases=test, integration-test", "verify"});
assertEquals(Optional.of(List.of("test", "integration-test")), options.skippedPhases());
}

@Test
void skippedPhasesShortOption() throws ParseException {
// Short option with hasArgs(): -sp consumes the next token as its value.
// No positional goal follows, so the value is unambiguously "test".
CommonsCliMavenOptions options = CommonsCliMavenOptions.parse("test", new String[] {"-sp", "test"});
assertEquals(Optional.of(List.of("test")), options.skippedPhases());
}

// -------------------------------------------------------------------------
// --skip-tests
// -------------------------------------------------------------------------

@Test
void skipTestsNotPresentByDefault() throws ParseException {
CommonsCliMavenOptions options = CommonsCliMavenOptions.parse("test", new String[] {"verify"});
assertEquals(Optional.empty(), options.skipTests());
}

@Test
void skipTestsLongOption() throws ParseException {
CommonsCliMavenOptions options = CommonsCliMavenOptions.parse("test", new String[] {"--skip-tests", "verify"});
assertEquals(Optional.of(Boolean.TRUE), options.skipTests());
}

@Test
void skipTestsShortOption() throws ParseException {
CommonsCliMavenOptions options = CommonsCliMavenOptions.parse("test", new String[] {"-st", "verify"});
assertEquals(Optional.of(Boolean.TRUE), options.skipTests());
}

// -------------------------------------------------------------------------
// --skip-phases and --skip-tests can be combined
// -------------------------------------------------------------------------

@Test
void skipPhasesAndSkipTestsCanCoexist() throws ParseException {
CommonsCliMavenOptions options =
CommonsCliMavenOptions.parse("test", new String[] {"--skip-phases=verify", "--skip-tests", "install"});
assertEquals(Optional.of(List.of("verify")), options.skippedPhases());
assertEquals(Optional.of(Boolean.TRUE), options.skipTests());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ public class DefaultMavenExecutionRequest implements MavenExecutionRequest {

private List<String> goals;

private List<String> skippedPhases;

private boolean useReactor = false;

private boolean recursive = true;
Expand Down Expand Up @@ -197,6 +199,7 @@ public static MavenExecutionRequest copy(MavenExecutionRequest original) {
copy.setInstallationToolchainsFile(original.getInstallationToolchainsFile());
copy.setBaseDirectory((original.getBaseDirectory() != null) ? new File(original.getBaseDirectory()) : null);
copy.setGoals(original.getGoals());
copy.setSkippedPhases(original.getSkippedPhases());
copy.setRecursive(original.isRecursive());
copy.setPom(original.getPom());
copy.setSystemProperties(original.getSystemProperties());
Expand Down Expand Up @@ -247,6 +250,24 @@ public List<String> getGoals() {
return goals;
}

@Override
public MavenExecutionRequest setSkippedPhases(List<String> skippedPhases) {
if (skippedPhases != null) {
this.skippedPhases = new ArrayList<>(skippedPhases);
} else {
this.skippedPhases = null;
}
return this;
}

@Override
public List<String> getSkippedPhases() {
if (skippedPhases == null) {
skippedPhases = new ArrayList<>();
}
return skippedPhases;
}

@Override
public Properties getSystemProperties() {
if (systemProperties == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,24 @@ public interface MavenExecutionRequest {

List<String> getGoals();

/**
* Sets the lifecycle phases whose mojo executions should be suppressed.
*
* @param skippedPhases list of phase names (e.g. {@code "test"}, {@code "integration-test"}),
* or {@code null} to clear
* @return this request
* @since 4.1.0
*/
MavenExecutionRequest setSkippedPhases(List<String> skippedPhases);

/**
* Returns the lifecycle phases whose mojo executions are suppressed.
*
* @return mutable list of phase names; never {@code null}
* @since 4.1.0
*/
List<String> getSkippedPhases();
Comment thread
gnodet marked this conversation as resolved.

// Properties

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -481,9 +481,9 @@ public Collection<Phase> phases() {
after(TEST_COMPILE),
after(TEST_RESOURCES),
dependencies(SCOPE_TEST, READY))),
phase(INTEGRATION_TEST)),
phase(INSTALL, after(PACKAGE)),
phase(DEPLOY, after(PACKAGE)))));
phase(INTEGRATION_TEST, after(BUILD))),
phase(INSTALL, after(VERIFY)),
phase(DEPLOY, after(VERIFY)))));
Comment thread
gnodet marked this conversation as resolved.
// END SNIPPET: default
}

Expand Down
Loading
Loading