diff --git a/versions-common/src/main/java/org/codehaus/mojo/versions/utils/CoreExtensionUtils.java b/versions-common/src/main/java/org/codehaus/mojo/versions/utils/CoreExtensionUtils.java deleted file mode 100644 index 8affa2a428..0000000000 --- a/versions-common/src/main/java/org/codehaus/mojo/versions/utils/CoreExtensionUtils.java +++ /dev/null @@ -1,65 +0,0 @@ -package org.codehaus.mojo.versions.utils; - -/* - * Copyright MojoHaus and Contributors - * - * Licensed 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. - */ - -import javax.xml.stream.XMLStreamException; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.Reader; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.stream.Stream; - -import org.apache.maven.model.Extension; -import org.apache.maven.project.MavenProject; -import org.codehaus.mojo.versions.model.io.stax.CoreExtensionsStaxReader; - -/** - * Utilities for reading and handling core extensions. - * - * @author Andrzej Jarmoniuk - * @since 2.15.0 - */ -public final class CoreExtensionUtils { - /** - * Reads the core extensions (not build extensions) configured for the given project - * from the {@code ${project}/.mvn/extensions.xml} file. - * - * @param project {@link MavenProject} instance - * @return stream of core extensions defined in the {@code ${project}/.mvn/extensions.xml} file - * @throws IOException thrown if a file I/O operation fails - * @throws XMLStreamException thrown if the file cannot be parsed - * @since 2.15.0 - */ - public static Stream getCoreExtensions(MavenProject project) throws IOException, XMLStreamException { - Path extensionsFile = project.getBasedir().toPath().resolve(".mvn/extensions.xml"); - if (!Files.isRegularFile(extensionsFile)) { - return Stream.empty(); - } - - try (Reader reader = new BufferedReader(new InputStreamReader(Files.newInputStream(extensionsFile)))) { - return new CoreExtensionsStaxReader() - .read(reader).getExtensions().stream().map(ex -> ExtensionBuilder.newBuilder() - .withGroupId(ex.getGroupId()) - .withArtifactId(ex.getArtifactId()) - .withVersion(ex.getVersion()) - .build()); - } - } -} diff --git a/versions-common/src/main/java/org/codehaus/mojo/versions/utils/ExtensionUtils.java b/versions-common/src/main/java/org/codehaus/mojo/versions/utils/ExtensionUtils.java new file mode 100644 index 0000000000..dc83257c59 --- /dev/null +++ b/versions-common/src/main/java/org/codehaus/mojo/versions/utils/ExtensionUtils.java @@ -0,0 +1,132 @@ +package org.codehaus.mojo.versions.utils; + +/* + * Copyright MojoHaus and Contributors + * + * Licensed 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. + */ + +import javax.xml.stream.XMLStreamException; +import javax.xml.transform.TransformerException; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Stream; + +import org.apache.commons.lang3.tuple.Pair; +import org.apache.maven.model.Build; +import org.apache.maven.model.Extension; +import org.apache.maven.model.Model; +import org.apache.maven.plugin.logging.Log; +import org.apache.maven.project.MavenProject; +import org.codehaus.mojo.versions.api.PomHelper; +import org.codehaus.mojo.versions.model.io.stax.CoreExtensionsStaxReader; +import org.codehaus.mojo.versions.rewriting.MutableXMLStreamReader; + +import static java.util.Optional.ofNullable; + +/** + * Utilities for reading and handling extensions. + */ +public final class ExtensionUtils { + /** + * Reads the core extensions configured for the given project + * from the {@code ${project}/.mvn/extensions.xml} file. + * + * @param project {@link MavenProject} instance + * @return stream of core extensions defined in the {@code ${project}/.mvn/extensions.xml} file + * @throws IOException thrown if a file I/O operation fails + * @throws XMLStreamException thrown if the file cannot be parsed + * @since 2.15.0 + */ + public static Stream getCoreExtensions(MavenProject project) throws IOException, XMLStreamException { + Path extensionsFile = project.getBasedir().toPath().resolve(".mvn/extensions.xml"); + if (!Files.isRegularFile(extensionsFile)) { + return Stream.empty(); + } + + try (Reader reader = new BufferedReader(new InputStreamReader(Files.newInputStream(extensionsFile)))) { + return new CoreExtensionsStaxReader() + .read(reader).getExtensions().stream().map(ex -> ExtensionBuilder.newBuilder() + .withGroupId(ex.getGroupId()) + .withArtifactId(ex.getArtifactId()) + .withVersion(ex.getVersion()) + .build()); + } + } + + /** + * Returns a stream of build extensions configured for the given project + * @param project {@link MavenProject} instance + * @param log {@link Log} instance + * @param interpolateProperties when {@code false}, will return extensions based on raw model, otherwise will + * process the interpolated model + * @return stream of build extensions + * @throws IOException if the model file can't be read + * @throws XMLStreamException if the model file can't be parsed + * @throws TransformerException if the model file can't be parsed + */ + public static Stream getBuildExtensions(MavenProject project, Log log, boolean interpolateProperties) + throws XMLStreamException, IOException, TransformerException { + if (interpolateProperties) { + return getInterpolatedBuildExtensions(project, log); + } else { + return PomHelper.getChildModels(project, log).values().stream() + .map(Model::getBuild) + .filter(Objects::nonNull) + .map(Build::getExtensions) + .map(List::stream) + .reduce(Stream::concat) + .orElse(Stream.empty()); + } + } + + private static Stream getInterpolatedBuildExtensions(MavenProject project, Log log) + throws IOException, XMLStreamException, TransformerException { + MutableXMLStreamReader pomReader = + new MutableXMLStreamReader(project.getFile().toPath()); + ModelNode rootNode = new ModelNode(PomHelper.getRawModel(pomReader.getSource(), project.getFile()), pomReader); + List rawModels = PomHelper.getRawModelTree(rootNode, log); + return rawModels.stream() + .filter(node -> Objects.nonNull(node.getModel())) + .filter(node -> ofNullable(node.getModel().getBuild()) + .map(Build::getExtensions) + .map(list -> !list.isEmpty()) + .orElse(false)) + .map(node -> Pair.of(node.getModel().getBuild().getExtensions(), getNodeProperties(node))) + .flatMap(pair -> pair.getLeft().stream().map(e -> Pair.of(e, pair.getRight()))) + .map(pair -> ExtensionBuilder.newBuilder() + .withGroupId(PomHelper.evaluate(pair.getLeft().getGroupId(), pair.getRight(), log)) + .withArtifactId(PomHelper.evaluate(pair.getLeft().getArtifactId(), pair.getRight(), log)) + .withVersion(PomHelper.evaluate(pair.getLeft().getVersion(), pair.getRight(), log)) + .build()); + } + + private static Map getNodeProperties(ModelNode node) { + Map properties = new HashMap<>(); + for (ModelNode p = node; p != null; p = p.getParent().orElse(null)) { + p.getModel() + .getProperties() + .forEach((key, value) -> properties.putIfAbsent(String.valueOf(key), String.valueOf(value))); + } + return properties; + } +} diff --git a/versions-common/src/test/java/org/codehaus/mojo/versions/utils/CoreExtensionUtilsTest.java b/versions-common/src/test/java/org/codehaus/mojo/versions/utils/ExtensionUtilsTest.java similarity index 90% rename from versions-common/src/test/java/org/codehaus/mojo/versions/utils/CoreExtensionUtilsTest.java rename to versions-common/src/test/java/org/codehaus/mojo/versions/utils/ExtensionUtilsTest.java index 9507567e8f..d2b507aa79 100644 --- a/versions-common/src/test/java/org/codehaus/mojo/versions/utils/CoreExtensionUtilsTest.java +++ b/versions-common/src/test/java/org/codehaus/mojo/versions/utils/ExtensionUtilsTest.java @@ -35,11 +35,11 @@ import static org.mockito.Mockito.when; /** - * Unit tests for {@link CoreExtensionUtils} + * Unit tests for {@link ExtensionUtils} * * @author Andrzej Jarmoniuk */ -class CoreExtensionUtilsTest { +class ExtensionUtilsTest { @Test void testNoExtensions() throws IOException, XMLStreamException { @@ -49,7 +49,7 @@ void testNoExtensions() throws IOException, XMLStreamException { new File("src/test/resources/org/codehaus/mojo/versions/utils/core-extensions/no-extensions")); MavenSession session = mock(MavenSession.class); when(session.getCurrentProject()).thenReturn(project); - assertThat(CoreExtensionUtils.getCoreExtensions(project).findAny(), is(Optional.empty())); + assertThat(ExtensionUtils.getCoreExtensions(project).findAny(), is(Optional.empty())); } @Test @@ -59,8 +59,7 @@ void testExtensionsFound() throws IOException, XMLStreamException { .thenReturn(new File("src/test/resources/org/codehaus/mojo/versions/utils/core-extensions")); MavenSession session = mock(MavenSession.class); when(session.getCurrentProject()).thenReturn(project); - Set extensions = - CoreExtensionUtils.getCoreExtensions(project).collect(Collectors.toSet()); + Set extensions = ExtensionUtils.getCoreExtensions(project).collect(Collectors.toSet()); assertThat( extensions, hasItems( diff --git a/versions-maven-plugin/src/it/it-display-plugin-updates-issue-1184-01/invoker.properties b/versions-maven-plugin/src/it/it-display-plugin-updates-issue-1184-01/invoker.properties new file mode 100644 index 0000000000..9e4e2c08f1 --- /dev/null +++ b/versions-maven-plugin/src/it/it-display-plugin-updates-issue-1184-01/invoker.properties @@ -0,0 +1,2 @@ +invoker.goals = ${project.groupId}:${project.artifactId}:${project.version}:display-plugin-updates +invoker.mavenOpts = -Dversions.outputFile=./output.txt -DoutputEncoding=UTF-8 diff --git a/versions-maven-plugin/src/it/it-display-plugin-updates-issue-1184-01/pom.xml b/versions-maven-plugin/src/it/it-display-plugin-updates-issue-1184-01/pom.xml new file mode 100644 index 0000000000..2e1bc18881 --- /dev/null +++ b/versions-maven-plugin/src/it/it-display-plugin-updates-issue-1184-01/pom.xml @@ -0,0 +1,21 @@ + + 4.0.0 + localhost + it-display-extension-updates-002 + + 1.0 + pom + + + + + localhost + dummy-api + 1.0 + + + + + diff --git a/versions-maven-plugin/src/it/it-display-plugin-updates-issue-1184-01/verify.groovy b/versions-maven-plugin/src/it/it-display-plugin-updates-issue-1184-01/verify.groovy new file mode 100644 index 0000000000..42c1f5430a --- /dev/null +++ b/versions-maven-plugin/src/it/it-display-plugin-updates-issue-1184-01/verify.groovy @@ -0,0 +1,3 @@ +def output = new File( basedir, "output.txt").text +assert !(output =~ /\Qlocalhost:dummy-api\E\s*\.*\s*\Q1.0\E/) + diff --git a/versions-maven-plugin/src/it/it-display-plugin-updates-issue-1184/.mvn/extensions.xml b/versions-maven-plugin/src/it/it-display-plugin-updates-issue-1184/.mvn/extensions.xml new file mode 100644 index 0000000000..7d31d60d13 --- /dev/null +++ b/versions-maven-plugin/src/it/it-display-plugin-updates-issue-1184/.mvn/extensions.xml @@ -0,0 +1,10 @@ + + + + localhost + dummy-api + 1.0 + + diff --git a/versions-maven-plugin/src/it/it-display-plugin-updates-issue-1184/invoker.properties b/versions-maven-plugin/src/it/it-display-plugin-updates-issue-1184/invoker.properties new file mode 100644 index 0000000000..9e4e2c08f1 --- /dev/null +++ b/versions-maven-plugin/src/it/it-display-plugin-updates-issue-1184/invoker.properties @@ -0,0 +1,2 @@ +invoker.goals = ${project.groupId}:${project.artifactId}:${project.version}:display-plugin-updates +invoker.mavenOpts = -Dversions.outputFile=./output.txt -DoutputEncoding=UTF-8 diff --git a/versions-maven-plugin/src/it/it-display-plugin-updates-issue-1184/pom.xml b/versions-maven-plugin/src/it/it-display-plugin-updates-issue-1184/pom.xml new file mode 100644 index 0000000000..28820da695 --- /dev/null +++ b/versions-maven-plugin/src/it/it-display-plugin-updates-issue-1184/pom.xml @@ -0,0 +1,11 @@ + + 4.0.0 + localhost + it-display-extension-updates-002 + + 1.0 + pom + + diff --git a/versions-maven-plugin/src/it/it-display-plugin-updates-issue-1184/verify.groovy b/versions-maven-plugin/src/it/it-display-plugin-updates-issue-1184/verify.groovy new file mode 100644 index 0000000000..42c1f5430a --- /dev/null +++ b/versions-maven-plugin/src/it/it-display-plugin-updates-issue-1184/verify.groovy @@ -0,0 +1,3 @@ +def output = new File( basedir, "output.txt").text +assert !(output =~ /\Qlocalhost:dummy-api\E\s*\.*\s*\Q1.0\E/) + diff --git a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/AbstractVersionsDisplayMojo.java b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/AbstractVersionsDisplayMojo.java index 9e005a8b2d..29bd534987 100644 --- a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/AbstractVersionsDisplayMojo.java +++ b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/AbstractVersionsDisplayMojo.java @@ -46,6 +46,13 @@ public abstract class AbstractVersionsDisplayMojo extends AbstractVersionsUpdate private static final int DEFAULT_OUTPUT_LINE_WIDTH = 80; + /** + * The width to pad info messages. + * + * @since 1.0-alpha-1 + */ + static final int INFO_PAD_SIZE = 72; + /** * If specified then the display output will be sent to the specified file. * diff --git a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/DisplayDependencyUpdatesMojo.java b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/DisplayDependencyUpdatesMojo.java index f256b28de5..28cb6e85ad 100644 --- a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/DisplayDependencyUpdatesMojo.java +++ b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/DisplayDependencyUpdatesMojo.java @@ -21,7 +21,6 @@ import javax.inject.Inject; -import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; @@ -30,10 +29,7 @@ import java.util.TreeSet; import org.apache.commons.lang3.StringUtils; -import org.apache.maven.artifact.ArtifactUtils; import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager; -import org.apache.maven.artifact.versioning.ArtifactVersion; -import org.apache.maven.artifact.versioning.Restriction; import org.apache.maven.model.Dependency; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; @@ -45,14 +41,14 @@ import org.codehaus.mojo.versions.api.VersionRetrievalException; import org.codehaus.mojo.versions.api.recording.ChangeRecorder; import org.codehaus.mojo.versions.filtering.WildcardMatcher; -import org.codehaus.mojo.versions.ordering.InvalidSegmentException; +import org.codehaus.mojo.versions.internal.DependencyUpdatesLoggingHelper; +import org.codehaus.mojo.versions.internal.DependencyUpdatesLoggingHelper.DependencyUpdatesResult; import org.codehaus.mojo.versions.rewriting.MutableXMLStreamReader; import org.codehaus.mojo.versions.utils.DependencyComparator; import org.codehaus.mojo.versions.utils.SegmentUtils; import org.eclipse.aether.RepositorySystem; import static java.util.Collections.emptySet; -import static java.util.Optional.empty; import static org.codehaus.mojo.versions.filtering.DependencyFilter.filterDependencies; import static org.codehaus.mojo.versions.utils.MavenProjectUtils.extractDependenciesFromDependencyManagement; import static org.codehaus.mojo.versions.utils.MavenProjectUtils.extractDependenciesFromPlugins; @@ -71,13 +67,6 @@ public class DisplayDependencyUpdatesMojo extends AbstractVersionsDisplayMojo { // ------------------------------ FIELDS ------------------------------ - /** - * The width to pad info messages. - * - * @since 1.0-alpha-1 - */ - private static final int INFO_PAD_SIZE = 72; - /** * Whether to process the dependencyManagement section of the project. * @@ -503,79 +492,33 @@ static void validateGAVList(List gavList, int numSections, String argume } } - private void logUpdates(Map updates, String section) { - List withUpdates = new ArrayList<>(); - List usingCurrent = new ArrayList<>(); - for (ArtifactVersions versions : updates.values()) { - String left = " " + ArtifactUtils.versionlessKey(versions.getArtifact()) + " "; - String currentVersion; - Optional latestVersion; - Optional unchangedSegment = SegmentUtils.determineUnchangedSegment( - allowMajorUpdates, allowMinorUpdates, allowIncrementalUpdates, getLog()); - if (versions.getCurrentVersion() != null) { - currentVersion = versions.getCurrentVersion().toString(); - try { - latestVersion = versions.getNewestVersion(currentVersion, unchangedSegment, allowSnapshots, false); - } catch (InvalidSegmentException e) { - latestVersion = empty(); - } - } else { - currentVersion = versions.getArtifact().getVersionRange().toString(); - ArtifactVersion actualVersion = - versions.getNewestVersion(versions.getArtifact().getVersionRange(), allowSnapshots); - Restriction newVersionRestriction; - try { - Restriction segmentRestriction = - versions.restrictionForUnchangedSegment(actualVersion, unchangedSegment, false); - newVersionRestriction = new Restriction( - actualVersion, - false, - segmentRestriction.getUpperBound(), - segmentRestriction.isUpperBoundInclusive()); - } catch (InvalidSegmentException e) { - throw new RuntimeException(e); - } - latestVersion = Optional.of(newVersionRestriction) - .map(restriction -> versions.getNewestVersion(restriction, allowSnapshots)); - } - String right = - " " + latestVersion.map(v -> currentVersion + " -> " + v).orElse(currentVersion); - List t = latestVersion.isPresent() ? withUpdates : usingCurrent; - if (right.length() + left.length() + 3 > INFO_PAD_SIZE + getOutputLineWidthOffset()) { - t.add(left + "..."); - t.add(StringUtils.leftPad(right, INFO_PAD_SIZE + getOutputLineWidthOffset())); - - } else { - t.add(StringUtils.rightPad(left, INFO_PAD_SIZE + getOutputLineWidthOffset() - right.length(), ".") - + right); - } - } + private void logUpdates(Map versionMap, String section) { + Optional unchangedSegment = SegmentUtils.determineUnchangedSegment( + allowMajorUpdates, allowMinorUpdates, allowIncrementalUpdates, getLog()); + DependencyUpdatesResult updates = DependencyUpdatesLoggingHelper.getDependencyUpdates( + versionMap, allowSnapshots, unchangedSegment, INFO_PAD_SIZE + getOutputLineWidthOffset()); if (isVerbose()) { - if (usingCurrent.isEmpty()) { - if (!withUpdates.isEmpty()) { + if (updates.getUsingLatest().isEmpty()) { + if (!updates.getWithUpdates().isEmpty()) { logLine(false, "No dependencies in " + section + " are using the newest version."); logLine(false, ""); } } else { logLine(false, "The following dependencies in " + section + " are using the newest version:"); - for (String s : usingCurrent) { - logLine(false, s); - } + updates.getUsingLatest().forEach(s -> logLine(false, s)); logLine(false, ""); } } - if (withUpdates.isEmpty()) { - if (!usingCurrent.isEmpty()) { + if (updates.getWithUpdates().isEmpty()) { + if (!updates.getUsingLatest().isEmpty()) { logLine(false, "No dependencies in " + section + " have newer versions."); logLine(false, ""); } } else { logLine(false, "The following dependencies in " + section + " have newer versions:"); - for (String withUpdate : withUpdates) { - logLine(false, withUpdate); - } + updates.getWithUpdates().forEach(s -> logLine(false, s)); logLine(false, ""); } } diff --git a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/DisplayExtensionUpdatesMojo.java b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/DisplayExtensionUpdatesMojo.java index 503b04be78..3beffc112d 100644 --- a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/DisplayExtensionUpdatesMojo.java +++ b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/DisplayExtensionUpdatesMojo.java @@ -20,48 +20,34 @@ import javax.xml.transform.TransformerException; import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; +import java.util.Collection; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Optional; -import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; -import org.apache.commons.lang3.StringUtils; -import org.apache.maven.artifact.ArtifactUtils; import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager; -import org.apache.maven.artifact.versioning.ArtifactVersion; -import org.apache.maven.model.Build; import org.apache.maven.model.Dependency; import org.apache.maven.model.Extension; -import org.apache.maven.model.Model; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.wagon.Wagon; import org.codehaus.mojo.versions.api.ArtifactVersions; -import org.codehaus.mojo.versions.api.PomHelper; import org.codehaus.mojo.versions.api.Segment; import org.codehaus.mojo.versions.api.VersionRetrievalException; import org.codehaus.mojo.versions.api.recording.ChangeRecorder; import org.codehaus.mojo.versions.filtering.DependencyFilter; import org.codehaus.mojo.versions.filtering.WildcardMatcher; +import org.codehaus.mojo.versions.internal.DependencyUpdatesLoggingHelper; import org.codehaus.mojo.versions.rewriting.MutableXMLStreamReader; -import org.codehaus.mojo.versions.utils.CoreExtensionUtils; import org.codehaus.mojo.versions.utils.DependencyBuilder; -import org.codehaus.mojo.versions.utils.ExtensionBuilder; -import org.codehaus.mojo.versions.utils.ModelNode; +import org.codehaus.mojo.versions.utils.ExtensionUtils; import org.codehaus.mojo.versions.utils.SegmentUtils; import org.eclipse.aether.RepositorySystem; -import static java.util.Optional.of; -import static org.codehaus.mojo.versions.api.Segment.MAJOR; - /** * Displays all build and core extensions that have newer versions available. * @@ -73,20 +59,11 @@ public class DisplayExtensionUpdatesMojo extends AbstractVersionsDisplayMojo { // ------------------------------ FIELDS ------------------------------ - /** - * The width to pad info messages. - * - * @since 1.0-alpha-1 - */ - private static final int INFO_PAD_SIZE = 72; - /** *

Specifies a comma-separated list of GAV patterns to consider * when looking for updates. If the trailing parts of the GAV are omitted, then can assume any value.

- * *

The wildcard "*" can be used as the only, first, last or both characters in each token. * The version token does support version ranges.

- * * Examples: {@code "mygroup:artifact:*"}, {@code "mygroup:artifact"}, {@code "mygroup"} * * @since 2.15.0 @@ -97,12 +74,9 @@ public class DisplayExtensionUpdatesMojo extends AbstractVersionsDisplayMojo { /** *

Specifies a comma-separated list of GAV patterns to NOT consider * when looking for updates. If the trailing parts of the GAV are omitted, then can assume any value.

- * *

This list is taken into account after {@link #extensionIncludes}

. - * *

The wildcard "*" can be used as the only, first, last or both characters in each token. * The version token does support version ranges.

- * * Examples: {@code "mygroup:artifact:*"}, {@code "mygroup:artifact"}, {@code "mygroup"} * * @since 2.15.0 @@ -120,7 +94,6 @@ public class DisplayExtensionUpdatesMojo extends AbstractVersionsDisplayMojo { /** *

Whether to allow the minor version number to be changed.

- * *

Note: {@code false} also implies {@linkplain #allowMajorUpdates} * to be {@code false}

* @@ -131,7 +104,6 @@ public class DisplayExtensionUpdatesMojo extends AbstractVersionsDisplayMojo { /** *

Whether to allow the incremental version number to be changed.

- * *

Note: {@code false} also implies {@linkplain #allowMajorUpdates} * and {@linkplain #allowMinorUpdates} to be {@code false}

* @@ -142,6 +114,7 @@ public class DisplayExtensionUpdatesMojo extends AbstractVersionsDisplayMojo { /** *

Whether to process core extensions. Default is {@code true}.

+ * * @since 2.15.0 */ @Parameter(property = "processCoreExtensions", defaultValue = "true") @@ -149,6 +122,7 @@ public class DisplayExtensionUpdatesMojo extends AbstractVersionsDisplayMojo { /** *

Whether to process build extensions. Default is {@code true}.

+ * * @since 2.15.0 */ @Parameter(property = "processBuildExtensions", defaultValue = "true") @@ -193,56 +167,14 @@ public void execute() throws MojoExecutionException, MojoFailureException { DependencyFilter includeFilter = DependencyFilter.parseFrom(extensionIncludes); DependencyFilter excludeFilter = DependencyFilter.parseFrom(extensionExcludes); - Set dependencies; try { - Stream extensions; - if (processCoreExtensions) { - extensions = CoreExtensionUtils.getCoreExtensions(project); - } else { - extensions = Stream.empty(); - } - if (processBuildExtensions) { - if (!interpolateProperties) { - extensions = Stream.concat( - extensions, - PomHelper.getChildModels(session.getCurrentProject(), getLog()).values().stream() - .map(Model::getBuild) - .filter(Objects::nonNull) - .map(Build::getExtensions) - .map(List::stream) - .reduce(Stream::concat) - .orElse(Stream.empty())); - } else { - List rawModels = getRawModels(); - for (ModelNode node : rawModels) { - if (node.getModel() == null) { - // unlikely - continue; - } - Map properties = new HashMap<>(); - for (ModelNode p = node; p != null; p = p.getParent().orElse(null)) { - p.getModel() - .getProperties() - .forEach((key, value) -> - properties.putIfAbsent(String.valueOf(key), String.valueOf(value))); - } - extensions = Stream.concat( - extensions, - Optional.ofNullable(node.getModel().getBuild()) - .map(Build::getExtensions) - .orElse(Collections.emptyList()) - .stream() - .map(e -> ExtensionBuilder.newBuilder() - .withGroupId(PomHelper.evaluate(e.getGroupId(), properties, getLog())) - .withArtifactId( - PomHelper.evaluate(e.getArtifactId(), properties, getLog())) - .withVersion(PomHelper.evaluate(e.getVersion(), properties, getLog())) - .build())); - } - } - } + Stream coreExtensions = + processCoreExtensions ? ExtensionUtils.getCoreExtensions(project) : Stream.empty(); + Stream buildExtensions = processBuildExtensions + ? ExtensionUtils.getBuildExtensions(getProject(), getLog(), interpolateProperties) + : Stream.empty(); - dependencies = extensions + Collection dependencies = Stream.concat(coreExtensions, buildExtensions) .map(e -> DependencyBuilder.newBuilder() .withGroupId(e.getGroupId()) .withArtifactId(e.getArtifactId()) @@ -251,102 +183,48 @@ public void execute() throws MojoExecutionException, MojoFailureException { .filter(includeFilter::matchersMatch) .filter(excludeFilter::matchersDontMatch) .collect(Collectors.toSet()); - } catch (IOException | XMLStreamException e) { - throw new MojoExecutionException(e.getMessage()); - } - if (dependencies.isEmpty()) { - getLog().info("Extensions set filtered by include- and exclude-filters is empty. Nothing to do."); - return; - } - try { + if (dependencies.isEmpty()) { + getLog().info("Extensions set filtered by include- and exclude-filters is empty. Nothing to do."); + return; + } + logUpdates(getHelper().lookupDependenciesUpdates(dependencies.stream(), true, true, allowSnapshots)); + } catch (IOException | XMLStreamException | TransformerException e) { + throw new MojoExecutionException(e.getMessage()); } catch (VersionRetrievalException e) { throw new MojoExecutionException(e.getMessage(), e); } } - private List getRawModels() throws MojoFailureException { - List rawModels; - try { - MutableXMLStreamReader pomReader = - new MutableXMLStreamReader(getProject().getFile().toPath()); - ModelNode rootNode = new ModelNode( - PomHelper.getRawModel(pomReader.getSource(), getProject().getFile()), pomReader); - rawModels = PomHelper.getRawModelTree(rootNode, getLog()); - } catch (IOException | XMLStreamException | TransformerException e) { - throw new MojoFailureException(e.getMessage(), e); - } - return rawModels; - } - - private Optional calculateUpdateScope() { - return of(SegmentUtils.determineUnchangedSegment( - allowMajorUpdates, allowMinorUpdates, allowIncrementalUpdates, getLog()) - .map(Segment::minorTo) - .orElse(MAJOR)); - } - - private void logUpdates(Map updates) { - List withUpdates = new ArrayList<>(); - List usingCurrent = new ArrayList<>(); - for (ArtifactVersions versions : updates.values()) { - String left = " " + ArtifactUtils.versionlessKey(versions.getArtifact()) + " "; - final String current; - ArtifactVersion latest; - if (versions.getCurrentVersion() != null) { - current = versions.getCurrentVersion().toString(); - latest = versions.getNewestUpdateWithinSegment(calculateUpdateScope(), allowSnapshots); - } else { - ArtifactVersion newestVersion = - versions.getNewestVersion(versions.getArtifact().getVersionRange(), allowSnapshots); - current = versions.getArtifact().getVersionRange().toString(); - latest = newestVersion == null - ? null - : versions.getNewestUpdateWithinSegment(newestVersion, calculateUpdateScope(), allowSnapshots); - if (latest != null - && ArtifactVersions.isVersionInRange( - latest, versions.getArtifact().getVersionRange())) { - latest = null; - } - } - String right = " " + (latest == null ? current : current + " -> " + latest); - List t = latest == null ? usingCurrent : withUpdates; - if (right.length() + left.length() + 3 > INFO_PAD_SIZE + getOutputLineWidthOffset()) { - t.add(left + "..."); - t.add(StringUtils.leftPad(right, INFO_PAD_SIZE + getOutputLineWidthOffset())); - - } else { - t.add(StringUtils.rightPad(left, INFO_PAD_SIZE + getOutputLineWidthOffset() - right.length(), ".") - + right); - } - } + private void logUpdates(Map versionMap) { + Optional unchangedSegment = SegmentUtils.determineUnchangedSegment( + allowMajorUpdates, allowMinorUpdates, allowIncrementalUpdates, getLog()); + DependencyUpdatesLoggingHelper.DependencyUpdatesResult updates = + DependencyUpdatesLoggingHelper.getDependencyUpdates( + versionMap, allowSnapshots, unchangedSegment, INFO_PAD_SIZE + getOutputLineWidthOffset()); if (verbose) { - if (usingCurrent.isEmpty()) { - if (!withUpdates.isEmpty()) { + if (updates.getUsingLatest().isEmpty()) { + if (!updates.getWithUpdates().isEmpty()) { logLine(false, "No extensions are using the newest version."); logLine(false, ""); } } else { logLine(false, "The following extensions are using the newest version:"); - for (String s : usingCurrent) { - logLine(false, s); - } + updates.getUsingLatest().forEach(s -> logLine(false, s)); logLine(false, ""); } } - if (withUpdates.isEmpty()) { - if (!usingCurrent.isEmpty()) { + if (updates.getWithUpdates().isEmpty()) { + if (!updates.getUsingLatest().isEmpty()) { logLine(false, "No extensions have newer versions."); logLine(false, ""); } } else { logLine(false, "The following extensions have newer versions:"); - for (String withUpdate : withUpdates) { - logLine(false, withUpdate); - } + updates.getWithUpdates().forEach(s -> logLine(false, s)); logLine(false, ""); } } diff --git a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/DisplayPluginUpdatesMojo.java b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/DisplayPluginUpdatesMojo.java index f48a72362e..324bda284b 100644 --- a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/DisplayPluginUpdatesMojo.java +++ b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/DisplayPluginUpdatesMojo.java @@ -24,9 +24,11 @@ import javax.xml.transform.TransformerException; import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.io.StringWriter; import java.nio.file.Paths; import java.util.ArrayList; @@ -38,11 +40,13 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.Stack; import java.util.TreeMap; import java.util.TreeSet; import java.util.regex.Pattern; +import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; @@ -55,6 +59,7 @@ import org.apache.maven.artifact.versioning.ArtifactVersion; import org.apache.maven.lifecycle.LifecycleExecutor; import org.apache.maven.model.BuildBase; +import org.apache.maven.model.Extension; import org.apache.maven.model.Model; import org.apache.maven.model.Plugin; import org.apache.maven.model.PluginManagement; @@ -85,6 +90,7 @@ import org.codehaus.mojo.versions.rewriting.MutableXMLStreamReader; import org.codehaus.mojo.versions.utils.DefaultArtifactVersionCache; import org.codehaus.mojo.versions.utils.DependencyBuilder; +import org.codehaus.mojo.versions.utils.ExtensionUtils; import org.codehaus.mojo.versions.utils.PluginComparator; import org.eclipse.aether.RepositorySystem; @@ -127,6 +133,7 @@ public class DisplayPluginUpdatesMojo extends AbstractVersionsDisplayMojo { public static final Pattern PATTERN_PROJECT_PLUGIN = Pattern.compile( "/project(/profiles/profile)?" + "((/build(/pluginManagement)?)|(/reporting))" + "/plugins/plugin"); + public static final String SUPERPOM_PATH = "org/apache/maven/model/pom-4.0.0.xml"; /** @@ -330,12 +337,17 @@ public void execute() throws MojoExecutionException, MojoFailureException { Map parentBuildPlugins = new HashMap<>(); Map parentReportPlugins = new HashMap<>(); - Set plugins = getPluginManagementPlugins( - superPomPluginManagement, - parentPlugins, - parentBuildPlugins, - parentReportPlugins, - pluginsWithVersionsSpecified); + Set plugins; + try { + plugins = getPluginManagementPlugins( + superPomPluginManagement, + parentPlugins, + parentBuildPlugins, + parentReportPlugins, + pluginsWithVersionsSpecified); + } catch (XMLStreamException | TransformerException | IOException e) { + throw new MojoFailureException(e.getMessage(), e); + } List pluginUpdates = new ArrayList<>(); List pluginLockdowns = new ArrayList<>(); @@ -742,7 +754,8 @@ public String toString() { /** * Returns a set of Strings which correspond to the plugin coordinates where there is a version specified. * - * @param pom a {@link MutableXMLStreamReader} instance for the pom project to get the plugins with versions specified. + * @param pom a {@link MutableXMLStreamReader} instance for the pom project to get the plugins with versions + * specified. * @return a set of Strings which correspond to the plugin coordinates where there is a version specified. */ private Set findPluginsWithVersionsSpecified(MutableXMLStreamReader pom) @@ -799,7 +812,8 @@ private Set findPluginsWithVersionsSpecified(MutableXMLStreamReader pom) * * @param pluginProject the plugin for which to retrieve the minimum Maven version which is required * @return The minimally required Maven version (never {@code null}) - * @see PluginReport + * @see PluginReport */ private ArtifactVersion getPrerequisitesMavenVersion(MavenProject pluginProject) { return ofNullable(pluginProject.getPrerequisites()) @@ -911,19 +925,36 @@ private Set getPluginManagementPlugins( Map parentPluginManagement, Map parentBuildPlugins, Map parentReportPlugins, - Set pluginsWithVersionsSpecified) { + Set pluginsWithVersionsSpecified) + throws XMLStreamException, IOException, TransformerException { + getLog().debug("Building list of project plugins..."); if (getLog().isDebugEnabled()) { - StringWriter origModel = new StringWriter(); - try { - origModel.write("Original model:\n"); - getProject().writeOriginalModel(origModel); - getLog().debug(origModel.toString()); + try (OutputStream outputStream = new ByteArrayOutputStream()) { + new MavenXpp3Writer().write(outputStream, getProject().getOriginalModel()); + getLog().debug("Original model:\n" + outputStream); } catch (IOException e) { - // ignore + getLog().debug(e); } } + + Set extensions = Stream.concat( + ExtensionUtils.getCoreExtensions(getProject()), + ExtensionUtils.getBuildExtensions(getProject(), getLog(), false)) + .collect(Collectors.toSet()); + if (getLog().isDebugEnabled()) { + getLog().debug("Extensions:" + + (extensions.isEmpty() + ? "(none)" + : extensions.stream() + .map(e -> "\n\t" + e.getGroupId() + ":" + e.getArtifactId() + + Optional.ofNullable(e.getVersion()) + .map(v -> ":") + .orElse("")) + .collect(Collectors.joining("\n")))); + } + ModelBuildingRequest modelBuildingRequest = new DefaultModelBuildingRequest(); modelBuildingRequest.setUserProperties(getProject().getProperties()); Model originalModel = modelInterpolator.interpolateModel( @@ -952,7 +983,7 @@ private Set getPluginManagementPlugins( debugPluginMap("after adding lifecycle plugins", plugins); ofNullable(originalModel.getBuild()) - .map(b -> getBuildPlugins(b, parentPluginManagement)) + .map(b -> getBuildPlugins(b, parentPluginManagement, extensions)) .ifPresent(p -> mergePluginsMap(plugins, p, parentPluginManagement)); debugPluginMap("after adding build plugins", plugins); @@ -971,7 +1002,7 @@ private Set getPluginManagementPlugins( debugPluginMap("after adding profile " + profile.getId() + " pluginManagement", plugins); ofNullable(profile.getBuild()) - .map(b -> getBuildPlugins(b, parentPluginManagement)) + .map(b -> getBuildPlugins(b, parentPluginManagement, extensions)) .ifPresent(p -> mergePluginsMap(plugins, p, parentBuildPlugins)); debugPluginMap("after adding profile " + profile.getId() + " build plugins", plugins); @@ -1000,11 +1031,18 @@ private static Stream getPluginManagementPlugins(BuildBase buildBase) { .orElse(Stream.empty()); } - private static Stream getBuildPlugins(BuildBase buildBase, Map parentPluginManagement) { + private static Stream getBuildPlugins( + BuildBase buildBase, Map parentPluginManagement, Set extensions) { return buildBase.getPlugins().stream() // removing plugins without a version // and with the parent also not defining it for them - .filter(plugin -> plugin.getVersion() != null || parentPluginManagement.get(plugin.getKey()) == null); + .filter(plugin -> plugin.getVersion() != null || parentPluginManagement.get(plugin.getKey()) == null) + .filter(plugin -> extensions.stream() + .noneMatch(e -> Objects.equals(plugin.getGroupId(), e.getGroupId()) + && Objects.equals(plugin.getArtifactId(), e.getArtifactId()) + && ofNullable(e.getVersion()) + .map(v -> v.equals(plugin.getVersion())) + .orElseGet(() -> Objects.isNull(plugin.getVersion())))); } private Stream getLifecyclePlugins(Map parentPluginManagement) { diff --git a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/internal/DependencyUpdatesLoggingHelper.java b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/internal/DependencyUpdatesLoggingHelper.java new file mode 100644 index 0000000000..96eb2acb08 --- /dev/null +++ b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/internal/DependencyUpdatesLoggingHelper.java @@ -0,0 +1,114 @@ +package org.codehaus.mojo.versions.internal; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.apache.commons.lang3.StringUtils; +import org.apache.maven.artifact.ArtifactUtils; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.Restriction; +import org.apache.maven.model.Dependency; +import org.codehaus.mojo.versions.DisplayDependencyUpdatesMojo; +import org.codehaus.mojo.versions.DisplayExtensionUpdatesMojo; +import org.codehaus.mojo.versions.api.ArtifactVersions; +import org.codehaus.mojo.versions.api.Segment; +import org.codehaus.mojo.versions.ordering.InvalidSegmentException; + +import static java.util.Optional.empty; + +/** + * Helper class for {@link DisplayDependencyUpdatesMojo} and {@link DisplayExtensionUpdatesMojo}, + * provides a way for formatting logging dependency updates + */ +public class DependencyUpdatesLoggingHelper { + + /** + * Compiles a {@link DependencyUpdatesResult} object containing dependency updates for the given dependency map + * and the given unchanged segment. + * @param updates map of available versions per dependency + * @param allowSnapshots whether snapshots should be allowed as updates + * @param unchangedSegment the most major segment not allowed to be updated or {@code Optional.empty()} if + * all segments are allowed to be updated + * @param maxLineWith maximum line width + * @return a {@link DependencyUpdatesResult} object containing the result + */ + public static DependencyUpdatesResult getDependencyUpdates( + Map updates, + boolean allowSnapshots, + Optional unchangedSegment, + int maxLineWith) { + List withUpdates = new ArrayList<>(); + List usingCurrent = new ArrayList<>(); + for (ArtifactVersions versions : updates.values()) { + String left = " " + ArtifactUtils.versionlessKey(versions.getArtifact()) + " "; + String currentVersion; + Optional latestVersion; + if (versions.getCurrentVersion() != null) { + currentVersion = versions.getCurrentVersion().toString(); + try { + latestVersion = versions.getNewestVersion(currentVersion, unchangedSegment, allowSnapshots, false); + } catch (InvalidSegmentException e) { + latestVersion = empty(); + } + } else { + currentVersion = versions.getArtifact().getVersionRange().toString(); + ArtifactVersion actualVersion = + versions.getNewestVersion(versions.getArtifact().getVersionRange(), allowSnapshots); + Restriction newVersionRestriction; + try { + Restriction segmentRestriction = + versions.restrictionForUnchangedSegment(actualVersion, unchangedSegment, false); + newVersionRestriction = new Restriction( + actualVersion, + false, + segmentRestriction.getUpperBound(), + segmentRestriction.isUpperBoundInclusive()); + } catch (InvalidSegmentException e) { + throw new RuntimeException(e); + } + latestVersion = Optional.of(newVersionRestriction) + .map(restriction -> versions.getNewestVersion(restriction, allowSnapshots)); + } + String right = + " " + latestVersion.map(v -> currentVersion + " -> " + v).orElse(currentVersion); + List t = latestVersion.isPresent() ? withUpdates : usingCurrent; + if (right.length() + left.length() + 3 > maxLineWith) { + t.add(left + "..."); + t.add(StringUtils.leftPad(right, maxLineWith)); + + } else { + t.add(StringUtils.rightPad(left, maxLineWith - right.length(), ".") + right); + } + } + + return new DependencyUpdatesResult() { + @Override + public List getUsingLatest() { + return usingCurrent; + } + + @Override + public List getWithUpdates() { + return withUpdates; + } + }; + } + + /** + * Defines the list of dependencies using current versions and the list of dependencies having updates + */ + public interface DependencyUpdatesResult { + + /** + * @return Dependencies using the latest version + */ + List getUsingLatest(); + + /** + * @return Dependencies with updates available + */ + List getWithUpdates(); + } +} diff --git a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/DisplayExtensionUpdatesMojoTest.java b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/DisplayExtensionUpdatesMojoTest.java index ed5ecfc686..17a6bafc68 100644 --- a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/DisplayExtensionUpdatesMojoTest.java +++ b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/DisplayExtensionUpdatesMojoTest.java @@ -181,4 +181,81 @@ public void testIncludesExcludesMakesSetEmpty() assertThat(Files.readAllLines(tempPath), empty()); } + + @Test + public void testMajorUpdates() + throws MojoExecutionException, MojoFailureException, IOException, IllegalAccessException { + setVariableValueToObject(mojo, "extensionExcludes", emptyList()); + setVariableValueToObject(mojo, "extensionIncludes", singletonList("*")); + mojo.getProject().setBuild(new Build()); + mojo.getProject() + .getBuild() + .setExtensions(Collections.singletonList(ExtensionBuilder.newBuilder() + .withGroupId("default-group") + .withArtifactId("artifactA") + .withVersion("1.0.0") + .build())); + + try (MockedStatic pomHelper = mockStatic(PomHelper.class)) { + pomHelper + .when(() -> PomHelper.getChildModels(ArgumentMatchers.any(MavenProject.class), any())) + .then(i -> Collections.singletonMap(null, ((MavenProject) i.getArgument(0)).getModel())); + mojo.execute(); + } + + assertThat(String.join("", Files.readAllLines(tempPath)), containsString("1.0.0 -> 2.0.0")); + } + + @Test + public void testMinorUpdates() + throws MojoExecutionException, MojoFailureException, IOException, IllegalAccessException { + setVariableValueToObject(mojo, "extensionExcludes", emptyList()); + setVariableValueToObject(mojo, "extensionIncludes", singletonList("*")); + mojo.getProject().setBuild(new Build()); + mojo.getProject() + .getBuild() + .setExtensions(Collections.singletonList(ExtensionBuilder.newBuilder() + .withGroupId("default-group") + .withArtifactId("artifactB") + .withVersion("1.0.0") + .build())); + + try (MockedStatic pomHelper = mockStatic(PomHelper.class)) { + pomHelper + .when(() -> PomHelper.getChildModels(ArgumentMatchers.any(MavenProject.class), any())) + .then(i -> Collections.singletonMap(null, ((MavenProject) i.getArgument(0)).getModel())); + mojo.execute(); + } + + assertThat(String.join("", Files.readAllLines(tempPath)), containsString("1.0.0 -> 1.1.0")); + } + + @Test + public void testIncrementalUpdates() + throws MojoExecutionException, MojoFailureException, IOException, IllegalAccessException { + setVariableValueToObject(mojo, "extensionExcludes", emptyList()); + setVariableValueToObject(mojo, "extensionIncludes", singletonList("*")); + mojo.getProject().setBuild(new Build()); + mojo.getProject() + .getBuild() + .setExtensions(Collections.singletonList(ExtensionBuilder.newBuilder() + .withGroupId("default-group") + .withArtifactId("artifactD") + .withVersion("1.0.0") + .build())); + mojo.repositorySystem = mockAetherRepositorySystem(new HashMap() { + { + put("artifactD", new String[] {"1.0.0", "1.0.1"}); + } + }); + + try (MockedStatic pomHelper = mockStatic(PomHelper.class)) { + pomHelper + .when(() -> PomHelper.getChildModels(ArgumentMatchers.any(MavenProject.class), any())) + .then(i -> Collections.singletonMap(null, ((MavenProject) i.getArgument(0)).getModel())); + mojo.execute(); + } + + assertThat(String.join("", Files.readAllLines(tempPath)), containsString("1.0.0 -> 1.0.1")); + } }