From 4d81dc71aaf5900728a8184fbe59f1eef6f3b81b Mon Sep 17 00:00:00 2001 From: Andrzej Jarmoniuk Date: Sun, 18 Dec 2022 14:28:57 +0100 Subject: [PATCH] Resolves #474: Add property support for UseDepVersion - adding a new parameter, processProperties, which will also update property values if dependencies are controlled by properties - changed to aggregator mode --- versions-common/pom.xml | 2 - .../codehaus/mojo/versions/api/PomHelper.java | 180 +++++++--- .../rewriting/ModifiedPomXMLEventReader.java | 4 + .../versions/utils/MavenProjectUtils.java | 8 +- .../mojo/versions/utils/ModelNode.java | 98 +++++ .../mojo/versions/api/PomHelperTest.java | 50 +++ .../findProperty/childA/grandchild/pom.xml | 15 + .../versions/api/findProperty/childA/pom.xml | 23 ++ .../versions/api/findProperty/childB/pom.xml | 19 + .../mojo/versions/api/findProperty/pom.xml | 19 + .../getRawModelTree/childA/grandchild/pom.xml | 16 + .../api/getRawModelTree/childA/pom.xml | 19 + .../api/getRawModelTree/childB/pom.xml | 15 + .../mojo/versions/api/getRawModelTree/pom.xml | 15 + .../it-use-dep-version-001/invoker.properties | 22 ++ .../src/it/it-use-dep-version-001/pom.xml | 38 ++ .../it/it-use-dep-version-001/verify.groovy | 21 ++ .../it-use-dep-version-002/invoker.properties | 22 ++ .../it-use-dep-version-002-child/pom.xml | 42 +++ .../src/it/it-use-dep-version-002/pom.xml | 42 +++ .../it/it-use-dep-version-002/verify.groovy | 24 ++ .../it-use-dep-version-003/invoker.properties | 22 ++ .../src/it/it-use-dep-version-003/pom.xml | 47 +++ .../it/it-use-dep-version-003/verify.groovy | 21 ++ .../it-use-dep-version-004/invoker.properties | 22 ++ .../src/it/it-use-dep-version-004/pom.xml | 51 +++ .../it/it-use-dep-version-004/verify.groovy | 22 ++ .../AbstractDependencyUpdatesReportMojo.java | 2 +- .../versions/AbstractVersionsUpdaterMojo.java | 15 +- .../versions/DisplayPluginUpdatesMojo.java | 12 +- .../mojo/versions/ResolveRangesMojo.java | 2 +- .../mojo/versions/UseDepVersionMojo.java | 337 ++++++++++++++++-- .../mojo/versions/UseDepVersionMojoTest.java | 288 ++++++++++++++- .../child-parent-redefinition/child/pom.xml | 26 ++ .../child-parent-redefinition/pom.xml | 33 ++ .../properties/child-parent/child/pom.xml | 22 ++ .../properties/child-parent/pom.xml | 33 ++ .../child/pom.xml | 26 ++ .../conflict-cancellation-profiles/pom.xml | 50 +++ .../conflict-cancellation/child/pom.xml | 26 ++ .../properties/conflict-cancellation/pom.xml | 43 +++ .../conflict-redefinition/child/pom.xml | 26 ++ .../properties/conflict-redefinition/pom.xml | 43 +++ .../properties/conflict/pom.xml | 43 +++ .../properties/simple-profiles/pom.xml | 47 +++ .../use-dep-version/properties/simple/pom.xml | 38 ++ 46 files changed, 1873 insertions(+), 118 deletions(-) create mode 100644 versions-common/src/main/java/org/codehaus/mojo/versions/utils/ModelNode.java create mode 100644 versions-common/src/test/resources/org/codehaus/mojo/versions/api/findProperty/childA/grandchild/pom.xml create mode 100644 versions-common/src/test/resources/org/codehaus/mojo/versions/api/findProperty/childA/pom.xml create mode 100644 versions-common/src/test/resources/org/codehaus/mojo/versions/api/findProperty/childB/pom.xml create mode 100644 versions-common/src/test/resources/org/codehaus/mojo/versions/api/findProperty/pom.xml create mode 100644 versions-common/src/test/resources/org/codehaus/mojo/versions/api/getRawModelTree/childA/grandchild/pom.xml create mode 100644 versions-common/src/test/resources/org/codehaus/mojo/versions/api/getRawModelTree/childA/pom.xml create mode 100644 versions-common/src/test/resources/org/codehaus/mojo/versions/api/getRawModelTree/childB/pom.xml create mode 100644 versions-common/src/test/resources/org/codehaus/mojo/versions/api/getRawModelTree/pom.xml create mode 100644 versions-maven-plugin/src/it/it-use-dep-version-001/invoker.properties create mode 100644 versions-maven-plugin/src/it/it-use-dep-version-001/pom.xml create mode 100644 versions-maven-plugin/src/it/it-use-dep-version-001/verify.groovy create mode 100644 versions-maven-plugin/src/it/it-use-dep-version-002/invoker.properties create mode 100644 versions-maven-plugin/src/it/it-use-dep-version-002/it-use-dep-version-002-child/pom.xml create mode 100644 versions-maven-plugin/src/it/it-use-dep-version-002/pom.xml create mode 100644 versions-maven-plugin/src/it/it-use-dep-version-002/verify.groovy create mode 100644 versions-maven-plugin/src/it/it-use-dep-version-003/invoker.properties create mode 100644 versions-maven-plugin/src/it/it-use-dep-version-003/pom.xml create mode 100644 versions-maven-plugin/src/it/it-use-dep-version-003/verify.groovy create mode 100644 versions-maven-plugin/src/it/it-use-dep-version-004/invoker.properties create mode 100644 versions-maven-plugin/src/it/it-use-dep-version-004/pom.xml create mode 100644 versions-maven-plugin/src/it/it-use-dep-version-004/verify.groovy create mode 100644 versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/child-parent-redefinition/child/pom.xml create mode 100644 versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/child-parent-redefinition/pom.xml create mode 100644 versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/child-parent/child/pom.xml create mode 100644 versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/child-parent/pom.xml create mode 100644 versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/conflict-cancellation-profiles/child/pom.xml create mode 100644 versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/conflict-cancellation-profiles/pom.xml create mode 100644 versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/conflict-cancellation/child/pom.xml create mode 100644 versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/conflict-cancellation/pom.xml create mode 100644 versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/conflict-redefinition/child/pom.xml create mode 100644 versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/conflict-redefinition/pom.xml create mode 100644 versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/conflict/pom.xml create mode 100644 versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/simple-profiles/pom.xml create mode 100644 versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/simple/pom.xml diff --git a/versions-common/pom.xml b/versions-common/pom.xml index f2b96ca2f..328cfa673 100644 --- a/versions-common/pom.xml +++ b/versions-common/pom.xml @@ -60,11 +60,9 @@ ${mavenVersion} provided - com.fasterxml.woodstox woodstox-core - test org.apache.commons diff --git a/versions-common/src/main/java/org/codehaus/mojo/versions/api/PomHelper.java b/versions-common/src/main/java/org/codehaus/mojo/versions/api/PomHelper.java index 1b041fc1b..974239a94 100644 --- a/versions-common/src/main/java/org/codehaus/mojo/versions/api/PomHelper.java +++ b/versions-common/src/main/java/org/codehaus/mojo/versions/api/PomHelper.java @@ -19,6 +19,7 @@ * under the License. */ +import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.events.XMLEvent; @@ -28,7 +29,10 @@ import java.io.InputStreamReader; import java.io.Reader; import java.io.StringReader; +import java.io.UncheckedIOException; import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; @@ -37,6 +41,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Properties; import java.util.Set; import java.util.Stack; @@ -45,6 +50,7 @@ import java.util.function.Consumer; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; import org.apache.maven.artifact.versioning.VersionRange; @@ -65,6 +71,7 @@ import org.apache.maven.project.ProjectBuildingRequest; import org.apache.maven.project.ProjectBuildingResult; import org.codehaus.mojo.versions.rewriting.ModifiedPomXMLEventReader; +import org.codehaus.mojo.versions.utils.ModelNode; import org.codehaus.mojo.versions.utils.RegexUtils; import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException; import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator; @@ -72,6 +79,7 @@ import org.codehaus.plexus.util.ReaderFactory; import org.codehaus.plexus.util.StringUtils; import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.codehaus.stax2.XMLInputFactory2; import static java.util.Collections.singletonMap; import static java.util.stream.IntStream.range; @@ -88,7 +96,7 @@ public class PomHelper { /** * Gets the raw model before any interpolation what-so-ever. * - * @param project The project to get the raw model for. + * @param project The project to getModel the raw model for. * @return The raw model. * @throws IOException if the file is not found or if the file does not parse. */ @@ -99,7 +107,7 @@ public static Model getRawModel(MavenProject project) throws IOException { /** * Gets the raw model before any interpolation what-so-ever. * - * @param moduleProjectFile The project file to get the raw model for. + * @param moduleProjectFile The project file to getModel the raw model for. * @return The raw model. * @throws IOException if the file is not found or if the file does not parse. */ @@ -115,21 +123,23 @@ public static Model getRawModel(File moduleProjectFile) throws IOException { /** * Gets the current raw model before any interpolation what-so-ever. * - * @param modifiedPomXMLEventReader The {@link ModifiedPomXMLEventReader} to get the raw model for. + * @param modifiedPomXMLEventReader The {@link ModifiedPomXMLEventReader} to getModel the raw model for. * @return The raw model. * @throws IOException if the file is not found or if the file does not parse. */ public static Model getRawModel(ModifiedPomXMLEventReader modifiedPomXMLEventReader) throws IOException { try (Reader reader = new StringReader(modifiedPomXMLEventReader.asStringBuilder().toString())) { - return getRawModel(reader); + Model result = getRawModel(reader); + result.setPomFile(new File(modifiedPomXMLEventReader.getPath())); + return result; } } /** * Gets the current raw model before any interpolation what-so-ever. * - * @param reader The {@link Reader} to get the raw model for. + * @param reader The {@link Reader} to getModel the raw model for. * @return The raw model. * @throws IOException if the file is not found or if the file does not parse. */ @@ -433,7 +443,7 @@ public static boolean setProjectParentVersion(final ModifiedPomXMLEventReader po * @param artifactId The artifactId of the dependency. * @param oldVersion The old version of the dependency. * @param newVersion The new version of the dependency. - * @param model The model to get the project properties from. + * @param model The model to getModel the project properties from. * @return true if a replacement was made. * @throws XMLStreamException if something went wrong. */ @@ -595,65 +605,67 @@ public static String evaluate(String expr, Map properties) { return null; } - String expression = stripTokens(expr); - if (expression.equals(expr)) { - int index = expr.indexOf("${"); - if (index >= 0) { - int lastIndex = expr.indexOf("}", index); - if (lastIndex >= 0) { - String retVal = expr.substring(0, index); + return extractExpression(expr) + .map(expression -> { + String value = properties.get(expression); + + if (value != null) { + int exprStartDelimiter = value.indexOf("${"); - if (index > 0 && expr.charAt(index - 1) == '$') { - retVal += expr.substring(index + 1, lastIndex + 1); + if (exprStartDelimiter >= 0) { + if (exprStartDelimiter > 0) { + value = value.substring(0, exprStartDelimiter) + + evaluate(value.substring(exprStartDelimiter), properties); + } else { + value = evaluate(value.substring(exprStartDelimiter), properties); + } + } } else { - retVal += evaluate(expr.substring(index, lastIndex + 1), properties); + // TODO find a way to log that and not use this System.out!! + // this class could be a component with logger injected !! + System.out.println("expression: " + expression + " no value "); + } + return value == null ? expr : value; + }) + .orElseGet(() -> { + int index = expr.indexOf("${"); + if (index >= 0) { + int lastIndex = expr.indexOf("}", index); + if (lastIndex >= 0) { + String retVal = expr.substring(0, index); + + if (index > 0 && expr.charAt(index - 1) == '$') { + retVal += expr.substring(index + 1, lastIndex + 1); + } else { + retVal += evaluate(expr.substring(index, lastIndex + 1), properties); + } + + retVal += evaluate(expr.substring(lastIndex + 1), properties); + return retVal; + } } - retVal += evaluate(expr.substring(lastIndex + 1), properties); - return retVal; - } - } - - // Was not an expression - if (expression.contains("$$")) { - return expression.replaceAll("\\$\\$", "\\$"); - } else { - return expression; - } - } - - String value = properties.get(expression); - - if (value != null) { - int exprStartDelimiter = value.indexOf("${"); - - if (exprStartDelimiter >= 0) { - if (exprStartDelimiter > 0) { - value = value.substring(0, exprStartDelimiter) - + evaluate(value.substring(exprStartDelimiter), properties); - } else { - value = evaluate(value.substring(exprStartDelimiter), properties); - } - } - } else { - // TODO find a way to log that and not use this System.out!! - // this class could be a component with logger injected !! - System.out.println("expression: " + expression + " no value "); - } - return value == null ? expr : value; + // Was not an expression + if (expr.contains("$$")) { + return expr.replaceAll("\\$\\$", "\\$"); + } else { + return expr; + } + }); } /** * Strips the expression token markers from the start and end of the string. * * @param expr the string (perhaps with token markers) - * @return the string (definately without token markers) + * @return the string (without token markers) if a property has been found, {@link Optional#empty()} + * otherwise */ - private static String stripTokens(String expr) { - if (expr.startsWith("${") && expr.indexOf("}") == expr.length() - 1) { - expr = expr.substring(2, expr.length() - 1); - } - return expr; + public static Optional extractExpression(String expr) { + return Optional.ofNullable(expr) + .map(String::trim) + .filter(e -> e.startsWith("${") && e.indexOf("}") == e.length() - 1) + .map(e -> e.substring(2, e.length() - 1)); } /** @@ -1179,7 +1191,7 @@ public static Set getAllChildModules(MavenProject project, Log logger) { * @return the set of all child modules of the project. */ public static Set getAllChildModules(Model model, Log logger) { - logger.debug("Finding child modules..."); + logger.debug("Finding child modules of " + model); Set childModules = new TreeSet<>(model.getModules()); model.getProfiles().forEach(profile -> childModules.addAll(profile.getModules())); debugModules(logger, "Child modules:", childModules); @@ -1326,6 +1338,64 @@ public static ProjectBuildingRequest createProjectBuilderRequest( }; } + /** + * Builds a {@link ModelNode} tree of raw models keyed by module path and returns a list of all nodes, + * ordered depth-first visiting order. The root node is always the first node of the list. + * + * @param rootNode The root node of the reactor + * @param logger logger to log parsing errors to + * @return the root node of the {@link ModelNode} of raw models relative to the project's basedir. + * @throws UncheckedIOException thrown if reading one of the models down the tree fails + */ + public static List getRawModelTree(ModelNode rootNode, Log logger) throws UncheckedIOException { + XMLInputFactory inputFactory = XMLInputFactory2.newInstance(); + inputFactory.setProperty(XMLInputFactory2.P_PRESERVE_LOCATION, Boolean.TRUE); + return getRawModelTree(rootNode, logger, inputFactory); + } + + private static List getRawModelTree(ModelNode rootNode, Log logger, XMLInputFactory inputFactory) + throws UncheckedIOException { + Path baseDir = rootNode.getModel().getPomFile().getParentFile().toPath(); + List result = new ArrayList<>(); + result.add(rootNode); + result.addAll(getAllChildModules(rootNode.getModel(), logger).stream() + .map(baseDir::resolve) + .map(path -> Files.isDirectory(path) ? path.resolve("pom.xml") : path) + .map(pomFile -> { + try { + ModifiedPomXMLEventReader pom = new ModifiedPomXMLEventReader( + new StringBuilder(new String(Files.readAllBytes(pomFile))), + inputFactory, + pomFile.toString()); + return new ModelNode(rootNode, getRawModel(pom), pom); + } catch (IOException e) { + throw new UncheckedIOException("Could not open " + pomFile, e); + } catch (XMLStreamException e) { + throw new RuntimeException("Could not parse " + pomFile, e); + } + }) + .flatMap(node -> getRawModelTree(node, logger, inputFactory).stream()) + .collect(Collectors.toList())); + return result; + } + + /** + * Traverses the module tree upwards searching for the closest definition of a property with the given name. + * + * @param propertyName name of the property to be found + * @param node model tree node at which the search should be started + * @return {@link Optional} object containing the model tree node containing the closest + * property definition, or {@link Optional#empty()} if none has been found + */ + public static Optional findProperty(String propertyName, ModelNode node) { + if (Optional.ofNullable(node.getModel().getProperties()) + .map(properties -> properties.getProperty(propertyName)) + .isPresent()) { + return Optional.of(node); + } + return node.getParent().flatMap(parent -> findProperty(propertyName, parent)); + } + /** * Builds a map of raw models keyed by module path. * diff --git a/versions-common/src/main/java/org/codehaus/mojo/versions/rewriting/ModifiedPomXMLEventReader.java b/versions-common/src/main/java/org/codehaus/mojo/versions/rewriting/ModifiedPomXMLEventReader.java index 99d0cb224..4798700c3 100644 --- a/versions-common/src/main/java/org/codehaus/mojo/versions/rewriting/ModifiedPomXMLEventReader.java +++ b/versions-common/src/main/java/org/codehaus/mojo/versions/rewriting/ModifiedPomXMLEventReader.java @@ -514,4 +514,8 @@ public Model parse() throws IOException, XmlPullParserException { MavenXpp3Reader reader = new MavenXpp3Reader(); return reader.read(new StringReader(pom.toString())); } + + public String getPath() { + return path; + } } diff --git a/versions-common/src/main/java/org/codehaus/mojo/versions/utils/MavenProjectUtils.java b/versions-common/src/main/java/org/codehaus/mojo/versions/utils/MavenProjectUtils.java index 5070b7ee8..730ee44ed 100644 --- a/versions-common/src/main/java/org/codehaus/mojo/versions/utils/MavenProjectUtils.java +++ b/versions-common/src/main/java/org/codehaus/mojo/versions/utils/MavenProjectUtils.java @@ -94,7 +94,7 @@ public static Set extractDependenciesFromDependencyManagement( log.debug("dependency from pom: " + dependency.getGroupId() + ":" + dependency.getArtifactId() + ":" + dependency.getVersion() + ":" + dependency.getScope()); if (dependency.getVersion() == null) { - // get parent and get the information from there. + // getModel parent and getModel the information from there. if (project.hasParent()) { log.debug("Reading parent dependencyManagement information"); DependencyManagement parentProjectDependencyManagement = processDependencyManagementTransitive @@ -112,10 +112,10 @@ public static Set extractDependenciesFromDependencyManagement( } } } else { - String message = "We can't get the version for the dependency " + dependency.getGroupId() + ":" - + dependency.getArtifactId() + " because there does not exist a parent."; + String message = "We can't getModel the version for the dependency " + dependency.getGroupId() + + ":" + dependency.getArtifactId() + " because there does not exist a parent."; log.error(message); - // Throw error because we will not able to get a version for a dependency. + // Throw error because we will not able to getModel a version for a dependency. throw new VersionRetrievalException(message); } } else { diff --git a/versions-common/src/main/java/org/codehaus/mojo/versions/utils/ModelNode.java b/versions-common/src/main/java/org/codehaus/mojo/versions/utils/ModelNode.java new file mode 100644 index 000000000..8497ee63b --- /dev/null +++ b/versions-common/src/main/java/org/codehaus/mojo/versions/utils/ModelNode.java @@ -0,0 +1,98 @@ +package org.codehaus.mojo.versions.utils; +/* + * 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. + */ + +import java.util.Optional; + +import org.apache.maven.model.Model; +import org.codehaus.mojo.versions.rewriting.ModifiedPomXMLEventReader; + +/** + * Represents a navigable tree of {@link Model} instances. + */ +public class ModelNode { + private ModelNode parent; + private Model item; + private ModifiedPomXMLEventReader pom; + + /** + * Creates a root node (without a parent). + * + * @param model {@link Model} instance + * @param pom {@link ModifiedPomXMLEventReader} instance + */ + public ModelNode(Model model, ModifiedPomXMLEventReader pom) { + this(null, model, pom); + } + + /** + * Creates a new instance with a parent node. + * + * @param parent parent node + * @param model {@link Model} instance + * @param pom {@link ModifiedPomXMLEventReader} instance + */ + public ModelNode(ModelNode parent, Model model, ModifiedPomXMLEventReader pom) { + this.parent = parent; + this.item = model; + this.pom = pom; + } + + /** + * Returns the {@link Model} instance associated with the given node. + * + * @return {@link Model} associated with the given node, never {@code null}. + */ + public Model getModel() { + return item; + } + + /** + * Gets the parent node. + * + * @return The parent node of this node or {@link Optional#empty()}, never {@code null}. + */ + public Optional getParent() { + return Optional.ofNullable(parent); + } + + /** + * Gets the {@link ModifiedPomXMLEventReader} instance + * + * @return the {@link ModifiedPomXMLEventReader} instance + */ + public ModifiedPomXMLEventReader getModifiedPomXMLEventReader() { + return pom; + } + + @Override + public boolean equals(Object o) { + if (o instanceof ModelNode) { + ModelNode other = (ModelNode) o; + return item.equals(other.item) && (parent == null || parent.equals(other.parent)) && pom.equals(other.pom); + } + return false; + } + + @Override + @SuppressWarnings("checkstyle:MagicNumber") + public int hashCode() { + return 13 * item.hashCode() + (parent == null ? 0 : 23 * parent.hashCode()) + 7 * pom.hashCode(); + } +} diff --git a/versions-common/src/test/java/org/codehaus/mojo/versions/api/PomHelperTest.java b/versions-common/src/test/java/org/codehaus/mojo/versions/api/PomHelperTest.java index 2db345689..0ed517cbb 100644 --- a/versions-common/src/test/java/org/codehaus/mojo/versions/api/PomHelperTest.java +++ b/versions-common/src/test/java/org/codehaus/mojo/versions/api/PomHelperTest.java @@ -27,20 +27,25 @@ import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Collections; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import org.apache.commons.io.FileUtils; import org.apache.maven.model.Model; import org.apache.maven.model.io.DefaultModelWriter; import org.apache.maven.model.io.ModelWriter; import org.apache.maven.model.io.xpp3.MavenXpp3Reader; +import org.apache.maven.plugin.logging.Log; import org.apache.maven.plugin.logging.SystemStreamLog; import org.apache.maven.plugin.testing.AbstractMojoTestCase; import org.apache.maven.plugin.testing.MojoRule; import org.apache.maven.project.MavenProject; import org.codehaus.mojo.versions.rewriting.ModifiedPomXMLEventReader; +import org.codehaus.mojo.versions.utils.ModelNode; import org.codehaus.stax2.XMLInputFactory2; import org.hamcrest.MatcherAssert; import org.junit.BeforeClass; @@ -52,6 +57,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.core.Is.is; +import static org.mockito.Mockito.mock; /** * Tests the methods of {@link PomHelper}. @@ -275,4 +281,48 @@ private Model createSimpleModel(String artifactId) { module.setModelVersion("4.0.0"); return module; } + + @Test + public void testGetRawModelTree() throws Exception { + Log log = mock(Log.class); + XMLInputFactory inputFactory = XMLInputFactory2.newInstance(); + inputFactory.setProperty(XMLInputFactory2.P_PRESERVE_LOCATION, Boolean.TRUE); + Path path = Paths.get("src/test/resources/org/codehaus/mojo/versions/api/getRawModelTree/pom.xml"); + ModifiedPomXMLEventReader pomReader = new ModifiedPomXMLEventReader( + new StringBuilder(new String(Files.readAllBytes(path))), inputFactory, path.toString()); + List rawModelTree = + PomHelper.getRawModelTree(new ModelNode(PomHelper.getRawModel(pomReader), pomReader), log); + assertThat( + rawModelTree.stream() + .map(ModelNode::getModel) + .map(Model::getArtifactId) + .collect(Collectors.joining(" ")), + is("grandparent childA grandchild childB")); + } + + @Test + public void testFindProperty() throws Exception { + Log log = mock(Log.class); + XMLInputFactory inputFactory = XMLInputFactory2.newInstance(); + inputFactory.setProperty(XMLInputFactory2.P_PRESERVE_LOCATION, Boolean.TRUE); + Path path = Paths.get("src/test/resources/org/codehaus/mojo/versions/api/findProperty/pom.xml"); + ModifiedPomXMLEventReader pomReader = new ModifiedPomXMLEventReader( + new StringBuilder(new String(Files.readAllBytes(path))), inputFactory, path.toString()); + List rawModelTree = + PomHelper.getRawModelTree(new ModelNode(PomHelper.getRawModel(pomReader), pomReader), log); + + ModelNode grandparent = rawModelTree.get(0); + assertThat(grandparent.getModel().getArtifactId(), is("grandparent")); + ModelNode childA = rawModelTree.get(1); + assertThat(childA.getModel().getArtifactId(), is("childA")); + ModelNode grandchild = rawModelTree.get(2); + assertThat(grandchild.getModel().getArtifactId(), is("grandchild")); + ModelNode childB = rawModelTree.get(3); + assertThat(childB.getModel().getArtifactId(), is("childB")); + assertThat(PomHelper.findProperty("a", grandchild).get(), is(childA)); + assertThat(PomHelper.findProperty("a", childA).get(), is(childA)); + assertThat(PomHelper.findProperty("a", grandparent).get(), is(grandparent)); + assertThat(PomHelper.findProperty("a", childB).get(), is(grandparent)); + assertThat(PomHelper.findProperty("b", childB).get(), is(childB)); + } } diff --git a/versions-common/src/test/resources/org/codehaus/mojo/versions/api/findProperty/childA/grandchild/pom.xml b/versions-common/src/test/resources/org/codehaus/mojo/versions/api/findProperty/childA/grandchild/pom.xml new file mode 100644 index 000000000..265cc68ad --- /dev/null +++ b/versions-common/src/test/resources/org/codehaus/mojo/versions/api/findProperty/childA/grandchild/pom.xml @@ -0,0 +1,15 @@ + + + 4.0.0 + + + default-group + childA + 1.0.0 + + + grandchild + pom + diff --git a/versions-common/src/test/resources/org/codehaus/mojo/versions/api/findProperty/childA/pom.xml b/versions-common/src/test/resources/org/codehaus/mojo/versions/api/findProperty/childA/pom.xml new file mode 100644 index 000000000..7ba7ea6f1 --- /dev/null +++ b/versions-common/src/test/resources/org/codehaus/mojo/versions/api/findProperty/childA/pom.xml @@ -0,0 +1,23 @@ + + + 4.0.0 + + + default-group + grandparent + 1.0.0 + + + childA + pom + + + a1 + + + + grandchild + + diff --git a/versions-common/src/test/resources/org/codehaus/mojo/versions/api/findProperty/childB/pom.xml b/versions-common/src/test/resources/org/codehaus/mojo/versions/api/findProperty/childB/pom.xml new file mode 100644 index 000000000..036456f6b --- /dev/null +++ b/versions-common/src/test/resources/org/codehaus/mojo/versions/api/findProperty/childB/pom.xml @@ -0,0 +1,19 @@ + + + 4.0.0 + + + default-group + grandparent + 1.0.0 + + + childB + pom + + + b + + diff --git a/versions-common/src/test/resources/org/codehaus/mojo/versions/api/findProperty/pom.xml b/versions-common/src/test/resources/org/codehaus/mojo/versions/api/findProperty/pom.xml new file mode 100644 index 000000000..6518e143e --- /dev/null +++ b/versions-common/src/test/resources/org/codehaus/mojo/versions/api/findProperty/pom.xml @@ -0,0 +1,19 @@ + + + 4.0.0 + default-group + grandparent + 1.0.0 + pom + + + a + + + + childA + childB + + diff --git a/versions-common/src/test/resources/org/codehaus/mojo/versions/api/getRawModelTree/childA/grandchild/pom.xml b/versions-common/src/test/resources/org/codehaus/mojo/versions/api/getRawModelTree/childA/grandchild/pom.xml new file mode 100644 index 000000000..798382a6f --- /dev/null +++ b/versions-common/src/test/resources/org/codehaus/mojo/versions/api/getRawModelTree/childA/grandchild/pom.xml @@ -0,0 +1,16 @@ + + + 4.0.0 + + + default-group + childA + 1.0.0 + + + grandchild + pom + + diff --git a/versions-common/src/test/resources/org/codehaus/mojo/versions/api/getRawModelTree/childA/pom.xml b/versions-common/src/test/resources/org/codehaus/mojo/versions/api/getRawModelTree/childA/pom.xml new file mode 100644 index 000000000..9ee57f2b8 --- /dev/null +++ b/versions-common/src/test/resources/org/codehaus/mojo/versions/api/getRawModelTree/childA/pom.xml @@ -0,0 +1,19 @@ + + + 4.0.0 + + + default-group + grandparent + 1.0.0 + + + childA + pom + + + grandchild + + diff --git a/versions-common/src/test/resources/org/codehaus/mojo/versions/api/getRawModelTree/childB/pom.xml b/versions-common/src/test/resources/org/codehaus/mojo/versions/api/getRawModelTree/childB/pom.xml new file mode 100644 index 000000000..4491f26b5 --- /dev/null +++ b/versions-common/src/test/resources/org/codehaus/mojo/versions/api/getRawModelTree/childB/pom.xml @@ -0,0 +1,15 @@ + + + 4.0.0 + + + default-group + grandparent + 1.0.0 + + + childB + pom + diff --git a/versions-common/src/test/resources/org/codehaus/mojo/versions/api/getRawModelTree/pom.xml b/versions-common/src/test/resources/org/codehaus/mojo/versions/api/getRawModelTree/pom.xml new file mode 100644 index 000000000..c11851573 --- /dev/null +++ b/versions-common/src/test/resources/org/codehaus/mojo/versions/api/getRawModelTree/pom.xml @@ -0,0 +1,15 @@ + + + 4.0.0 + default-group + grandparent + 1.0.0 + pom + + + childA + childB + + diff --git a/versions-maven-plugin/src/it/it-use-dep-version-001/invoker.properties b/versions-maven-plugin/src/it/it-use-dep-version-001/invoker.properties new file mode 100644 index 000000000..a6930739c --- /dev/null +++ b/versions-maven-plugin/src/it/it-use-dep-version-001/invoker.properties @@ -0,0 +1,22 @@ +# +# 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. +# + +invoker.goals= ${project.groupId}:${project.artifactId}:${project.version}:use-dep-version +invoker.mavenOpts = -Dincludes=localhost -DdepVersion=1.0.1 +invoker.buildResult = success diff --git a/versions-maven-plugin/src/it/it-use-dep-version-001/pom.xml b/versions-maven-plugin/src/it/it-use-dep-version-001/pom.xml new file mode 100644 index 000000000..dc2c4be7f --- /dev/null +++ b/versions-maven-plugin/src/it/it-use-dep-version-001/pom.xml @@ -0,0 +1,38 @@ + + + + 4.0.0 + + localhost + it-use-dep-version-001 + 1.0 + pom + + + + localhost + dummy-api + 1.0 + + + + diff --git a/versions-maven-plugin/src/it/it-use-dep-version-001/verify.groovy b/versions-maven-plugin/src/it/it-use-dep-version-001/verify.groovy new file mode 100644 index 000000000..e0c7f6eb4 --- /dev/null +++ b/versions-maven-plugin/src/it/it-use-dep-version-001/verify.groovy @@ -0,0 +1,21 @@ +/* + * 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. + */ + +def project = new XmlSlurper().parse( new File( basedir, 'pom.xml' ) ) +assert project.dependencies.dependency.version == '1.0.1' diff --git a/versions-maven-plugin/src/it/it-use-dep-version-002/invoker.properties b/versions-maven-plugin/src/it/it-use-dep-version-002/invoker.properties new file mode 100644 index 000000000..a6930739c --- /dev/null +++ b/versions-maven-plugin/src/it/it-use-dep-version-002/invoker.properties @@ -0,0 +1,22 @@ +# +# 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. +# + +invoker.goals= ${project.groupId}:${project.artifactId}:${project.version}:use-dep-version +invoker.mavenOpts = -Dincludes=localhost -DdepVersion=1.0.1 +invoker.buildResult = success diff --git a/versions-maven-plugin/src/it/it-use-dep-version-002/it-use-dep-version-002-child/pom.xml b/versions-maven-plugin/src/it/it-use-dep-version-002/it-use-dep-version-002-child/pom.xml new file mode 100644 index 000000000..2bbd8891d --- /dev/null +++ b/versions-maven-plugin/src/it/it-use-dep-version-002/it-use-dep-version-002-child/pom.xml @@ -0,0 +1,42 @@ + + + + 4.0.0 + + + localhost + it-use-dep-version-002 + 1.0 + + + it-use-dep-version-002-child + pom + + + + localhost + dummy-api + 1.0 + + + + diff --git a/versions-maven-plugin/src/it/it-use-dep-version-002/pom.xml b/versions-maven-plugin/src/it/it-use-dep-version-002/pom.xml new file mode 100644 index 000000000..6acd5cdc4 --- /dev/null +++ b/versions-maven-plugin/src/it/it-use-dep-version-002/pom.xml @@ -0,0 +1,42 @@ + + + + 4.0.0 + + localhost + it-use-dep-version-002 + 1.0 + pom + + + it-use-dep-version-002-child + + + + + localhost + dummy-api + 1.0 + + + + diff --git a/versions-maven-plugin/src/it/it-use-dep-version-002/verify.groovy b/versions-maven-plugin/src/it/it-use-dep-version-002/verify.groovy new file mode 100644 index 000000000..4f3efccf2 --- /dev/null +++ b/versions-maven-plugin/src/it/it-use-dep-version-002/verify.groovy @@ -0,0 +1,24 @@ +/* + * 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. + */ + +def parent = new XmlSlurper().parse( new File( basedir, 'pom.xml' ) ) +assert parent.dependencies.dependency.version == '1.0.1' + +def child = new XmlSlurper().parse( new File( basedir, 'it-use-dep-version-002-child/pom.xml' ) ) +assert child.dependencies.dependency.version == '1.0.1' diff --git a/versions-maven-plugin/src/it/it-use-dep-version-003/invoker.properties b/versions-maven-plugin/src/it/it-use-dep-version-003/invoker.properties new file mode 100644 index 000000000..a6930739c --- /dev/null +++ b/versions-maven-plugin/src/it/it-use-dep-version-003/invoker.properties @@ -0,0 +1,22 @@ +# +# 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. +# + +invoker.goals= ${project.groupId}:${project.artifactId}:${project.version}:use-dep-version +invoker.mavenOpts = -Dincludes=localhost -DdepVersion=1.0.1 +invoker.buildResult = success diff --git a/versions-maven-plugin/src/it/it-use-dep-version-003/pom.xml b/versions-maven-plugin/src/it/it-use-dep-version-003/pom.xml new file mode 100644 index 000000000..5e2704713 --- /dev/null +++ b/versions-maven-plugin/src/it/it-use-dep-version-003/pom.xml @@ -0,0 +1,47 @@ + + + + 4.0.0 + + localhost + it-use-dep-version-003 + 1.0 + pom + + + + test-profile + + true + + + + + localhost + dummy-api + 1.0 + + + + + + diff --git a/versions-maven-plugin/src/it/it-use-dep-version-003/verify.groovy b/versions-maven-plugin/src/it/it-use-dep-version-003/verify.groovy new file mode 100644 index 000000000..5c8f82d14 --- /dev/null +++ b/versions-maven-plugin/src/it/it-use-dep-version-003/verify.groovy @@ -0,0 +1,21 @@ +/* + * 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. + */ + +def project = new XmlSlurper().parse( new File( basedir, 'pom.xml' ) ) +assert project.profiles.profile.dependencies.dependency.version == '1.0.1' diff --git a/versions-maven-plugin/src/it/it-use-dep-version-004/invoker.properties b/versions-maven-plugin/src/it/it-use-dep-version-004/invoker.properties new file mode 100644 index 000000000..219d25748 --- /dev/null +++ b/versions-maven-plugin/src/it/it-use-dep-version-004/invoker.properties @@ -0,0 +1,22 @@ +# +# 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. +# + +invoker.goals= ${project.groupId}:${project.artifactId}:${project.version}:use-dep-version +invoker.mavenOpts = -Dincludes=localhost -DdepVersion=1.0.1 -DprocessProperties=true +invoker.buildResult = success diff --git a/versions-maven-plugin/src/it/it-use-dep-version-004/pom.xml b/versions-maven-plugin/src/it/it-use-dep-version-004/pom.xml new file mode 100644 index 000000000..b63506654 --- /dev/null +++ b/versions-maven-plugin/src/it/it-use-dep-version-004/pom.xml @@ -0,0 +1,51 @@ + + + + 4.0.0 + + localhost + it-use-dep-version-004 + 1.0 + pom + + + 1.0 + + + + + test-profile + + true + + + + + localhost + dummy-api + ${revision} + + + + + + diff --git a/versions-maven-plugin/src/it/it-use-dep-version-004/verify.groovy b/versions-maven-plugin/src/it/it-use-dep-version-004/verify.groovy new file mode 100644 index 000000000..f5eb90562 --- /dev/null +++ b/versions-maven-plugin/src/it/it-use-dep-version-004/verify.groovy @@ -0,0 +1,22 @@ +/* + * 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. + */ + +def project = new XmlSlurper().parse( new File( basedir, 'pom.xml' ) ) +assert project.properties.revision == '1.0.1' +assert project.profiles.profile.dependencies.dependency.version == '${revision}' diff --git a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/AbstractDependencyUpdatesReportMojo.java b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/AbstractDependencyUpdatesReportMojo.java index ac9b15b08..32f583357 100644 --- a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/AbstractDependencyUpdatesReportMojo.java +++ b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/AbstractDependencyUpdatesReportMojo.java @@ -193,7 +193,7 @@ protected void handleDependencyManagementTransitive( } else { if (project.getOriginalModel().getDependencyManagement() != null && project.getOriginalModel().getDependencyManagement().getDependencies() != null) { - // Using the original model to get the original dependencyManagement entries and + // Using the original model to getModel the original dependencyManagement entries and // not the interpolated model. // TODO: I'm not 100% sure if this will work correctly in all cases. for (Dependency dep : diff --git a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/AbstractVersionsUpdaterMojo.java b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/AbstractVersionsUpdaterMojo.java index caee51d72..5be9c69ff 100644 --- a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/AbstractVersionsUpdaterMojo.java +++ b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/AbstractVersionsUpdaterMojo.java @@ -119,7 +119,7 @@ public abstract class AbstractVersionsUpdaterMojo extends AbstractMojo { * @since 1.0-alpha-3 */ @Parameter(property = "generateBackupPoms", defaultValue = "true") - private boolean generateBackupPoms; + protected boolean generateBackupPoms; /** * Whether to allow snapshots when searching for the latest version of an artifact. @@ -300,19 +300,6 @@ protected ArtifactVersion findLatestVersion( return artifactVersions.getNewestVersion(versionRange, null, includeSnapshots, false); } - /** - * Gets the property value that is defined in the pom. This is an extension point to allow updating a file external - * to the reactor. - * - * @param pom The pom. - * @param property The property. - * @return The value as defined in the pom or null if not defined. - * @since 1.0-alpha-1 - */ - protected String getPropertyValue(StringBuilder pom, String property) { - return project.getProperties().getProperty(property); - } - /** * Processes the specified file. This is an extension point to allow updating a file external to the reactor. * 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 8f69403f4..cbe735071 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 @@ -248,7 +248,7 @@ private Map getSuperPomPluginManagement() throws MojoExecutionEx /** * Gets the plugin management plugins of a specific project. * - * @param model the model to get the plugin management plugins from. + * @param model the model to getModel the plugin management plugins from. * @return The map of effective plugin versions keyed by coordinates. * @since 1.0-alpha-1 */ @@ -729,7 +729,7 @@ public String toString() { /** * Returns a set of Strings which correspond to the plugin coordinates where there is a version specified. * - * @param pomContents The project to get the plugins with versions specified. + * @param pomContents The project to getModel the plugins with versions specified. * @param path Path that points to the source of the XML * @return a set of Strings which correspond to the plugin coordinates where there is a version specified. */ @@ -818,7 +818,7 @@ private Map getPluginsFromBuild(BuildBase build, boolean onlyInc /** * Gets the build plugins of a specific project. * - * @param model the model to get the build plugins from. + * @param model the model to getModel the build plugins from. * @param onlyIncludeInherited {@code true} to only return the plugins definitions that will be inherited by * child projects. * @return The map of effective plugin versions keyed by coordinates. @@ -850,7 +850,7 @@ private static boolean getPluginInherited(Object plugin) { /** * Returns the lifecycle plugins of a specific project. * - * @param project the project to get the lifecycle plugins from. + * @param project the project to getModel the lifecycle plugins from. * @return The map of effective plugin versions keyed by coordinates. * @throws org.apache.maven.plugin.MojoExecutionException if things go wrong. * @since 1.0-alpha-1 @@ -887,7 +887,7 @@ private Set getBoundPlugins(MavenProject project) { /** * Returns all the parent projects of the specified project, with the root project first. * - * @param project The maven project to get the parents of + * @param project The maven project to getModel the parents of * @return the parent projects of the specified project, with the root project first. * @throws org.apache.maven.plugin.MojoExecutionException if the super-pom could not be created. * @since 1.0-alpha-1 @@ -1149,7 +1149,7 @@ private static List toPlugins(List reportPlugins) { /** * Gets the report plugins of a specific project. * - * @param model the model to get the report plugins from. + * @param model the model to getModel the report plugins from. * @param onlyIncludeInherited true to only return the plugins definitions that will be inherited by * child projects. * @return The map of effective plugin versions keyed by coordinates. diff --git a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/ResolveRangesMojo.java b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/ResolveRangesMojo.java index 04b043f37..f9ccfdad0 100644 --- a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/ResolveRangesMojo.java +++ b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/ResolveRangesMojo.java @@ -140,7 +140,7 @@ public ResolveRangesMojo( */ protected void update(ModifiedPomXMLEventReader pom) throws MojoExecutionException, MojoFailureException, XMLStreamException, VersionRetrievalException { - // Note we have to get the dependencies from the model because the dependencies in the + // Note we have to getModel the dependencies from the model because the dependencies in the // project may have already had their range resolved [MNG-4138] if (hasDependencyManagement() && hasDependenciesInDependencyManagement() diff --git a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/UseDepVersionMojo.java b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/UseDepVersionMojo.java index 70c06daf6..b0a117afe 100644 --- a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/UseDepVersionMojo.java +++ b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/UseDepVersionMojo.java @@ -22,27 +22,46 @@ import javax.inject.Inject; import javax.xml.stream.XMLStreamException; +import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.TreeSet; +import java.util.stream.Collectors; import org.apache.maven.artifact.Artifact; import org.apache.maven.model.Dependency; -import org.apache.maven.model.DependencyManagement; +import org.apache.maven.model.Model; +import org.apache.maven.model.ModelBase; 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.repository.RepositorySystem; 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.VersionRetrievalException; import org.codehaus.mojo.versions.api.recording.ChangeRecord; import org.codehaus.mojo.versions.api.recording.ChangeRecorder; import org.codehaus.mojo.versions.rewriting.ModifiedPomXMLEventReader; +import org.codehaus.mojo.versions.utils.DependencyComparator; +import org.codehaus.mojo.versions.utils.ModelNode; +import org.codehaus.plexus.util.FileUtils; +import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; +import static java.util.Optional.ofNullable; +import static org.codehaus.mojo.versions.api.recording.ChangeRecord.ChangeKind.DEPENDENCY; +import static org.codehaus.mojo.versions.api.recording.ChangeRecord.ChangeKind.DEPENDENCY_MANAGEMENT; +import static org.codehaus.mojo.versions.api.recording.ChangeRecord.ChangeKind.PARENT; /** * Updates a dependency to a specific version. @@ -52,7 +71,7 @@ * @author Dan Arcari * @since 2.3 */ -@Mojo(name = "use-dep-version", threadSafe = true) +@Mojo(name = "use-dep-version", aggregator = true, threadSafe = true) public class UseDepVersionMojo extends AbstractVersionsDependencyUpdaterMojo { /** @@ -62,12 +81,28 @@ public class UseDepVersionMojo extends AbstractVersionsDependencyUpdaterMojo { protected String depVersion; /** - * If set to true, will use whatever version is supplied without attempting to validate that such a version is - * obtainable from the repository chain. + * If set to {@code true}, will use whatever version is supplied without attempting to validate that such + * a version is obtainable from the repository chain. */ @Parameter(property = "forceVersion", defaultValue = "false") protected boolean forceVersion; + /** + *

Will augment normal processing by, if a dependency value is set using a property, trying to update + * the value of the property.

+ *

If the property value is specified directly, will process it normally (as with {@code processProperties} being + * {@code false}. If the property being updated is redefined in the reactor tree, will only change the property + * value which lies closest to the dependency being updated. If the same property is also used to set + * the value of another dependency, will not update that property value, and log a warning instead. + * Finally, if the property value is specified in a parent file which is outside of the project, will log + * a message.

+ *

Default is {@code false}.

+ * + * @since 2.15.0 + */ + @Parameter(property = "processProperties", defaultValue = "false") + protected boolean processProperties; + @Inject public UseDepVersionMojo( RepositorySystem repositorySystem, @@ -78,9 +113,8 @@ public UseDepVersionMojo( } @Override - protected void update(ModifiedPomXMLEventReader pom) - throws MojoExecutionException, MojoFailureException, XMLStreamException, VersionRetrievalException { - + protected void validateInput() throws MojoExecutionException { + super.validateInput(); if (depVersion == null || depVersion.equals("")) { throw new IllegalArgumentException( "depVersion must be supplied with use-specific-version, and cannot be blank."); @@ -92,57 +126,302 @@ protected void update(ModifiedPomXMLEventReader pom) + "Please specify a value for the 'includes' parameter, " + "or use -DforceVersion=true to override this check."); } + } + @Override + protected void update(ModifiedPomXMLEventReader pom) + throws MojoExecutionException, MojoFailureException, XMLStreamException, VersionRetrievalException { + // not used + } + + @Override + public void execute() throws MojoExecutionException, MojoFailureException { + validateInput(); + List rawModels; try { - if (isProcessingDependencyManagement()) { - DependencyManagement dependencyManagement = - PomHelper.getRawModel(getProject()).getDependencyManagement(); - if (dependencyManagement != null) { - useDepVersion( - pom, dependencyManagement.getDependencies(), ChangeRecord.ChangeKind.DEPENDENCY_MANAGEMENT); - } + ModifiedPomXMLEventReader pomReader = newModifiedPomXER( + new StringBuilder( + new String(Files.readAllBytes(getProject().getFile().toPath()))), + getProject().getFile().toPath().toString()); + ModelNode rootNode = new ModelNode(PomHelper.getRawModel(pomReader), pomReader); + rawModels = PomHelper.getRawModelTree(rootNode, getLog()); + // reversing to process depth-first + Collections.reverse(rawModels); + } catch (IOException e) { + throw new MojoFailureException(e.getMessage(), e); + } + + try { + Set propertyBacklog = new HashSet<>(); + Map> propertyConflicts = new HashMap<>(); + for (ModelNode node : rawModels) { + processModel(node, propertyBacklog, propertyConflicts); + } + propertyBacklog.forEach(p -> { + getLog().warn("Not updating property ${" + p + "}: defined in parent"); + }); + } catch (RuntimeException e) { + if (e.getCause() instanceof MojoFailureException) { + throw (MojoFailureException) e.getCause(); + } else if (e.getCause() instanceof MojoExecutionException) { + throw (MojoExecutionException) e.getCause(); } + throw e; + } + } + + /** + * Processes a single model and POM file associated with it + * @param node tree node to process + * @param propertyBacklog a {@link Set} instance used to store dependencies to be updated, but which were not found + * in the current subtree. These properties need to be carried over to the parent node for + * processing. + * @param propertyConflicts an {@link Map} instance to store properties + * which are associated with dependencies which do not fit the filter and thus may not + * be changed. This is then used for conflict detection if a dependency to be changed + * used one of these properties. Such a change is not allowed and must be reported instead. + * @return {@code true} if the file has been changed + */ + protected boolean processModel( + ModelNode node, Set propertyBacklog, Map> propertyConflicts) + throws MojoFailureException, MojoExecutionException { + // 1) process the properties carried over from children + propertyBacklog.removeIf(p -> updatePropertyValue(node, p)); - if (getProject().getDependencies() != null && isProcessingDependencies()) { - useDepVersion(pom, getProject().getDependencies(), ChangeRecord.ChangeKind.DEPENDENCY); + // 2) process dependencies and properties from this node + try { + if (isProcessingDependencyManagement() && node.getModel().getDependencyManagement() != null) { + useDepVersion( + node, + node.getModel().getDependencyManagement().getDependencies(), + DEPENDENCY_MANAGEMENT, + propertyBacklog, + propertyConflicts); + } + + if (isProcessingDependencies()) { + useDepVersion(node, getDependencies(node.getModel()), DEPENDENCY, propertyBacklog, propertyConflicts); } if (getProject().getParent() != null && isProcessingParent()) { - useDepVersion(pom, singletonList(getParentDependency()), ChangeRecord.ChangeKind.PARENT); + useDepVersion(node, singletonList(getParentDependency()), PARENT, propertyBacklog, propertyConflicts); + } + } catch (XMLStreamException e) { + throw new MojoFailureException( + "Unable to parse the pom " + node.getModel().getPomFile(), e); + } catch (VersionRetrievalException e) { + throw new MojoFailureException( + "Unable to retrieve a dependency version while processing " + + node.getModel().getPomFile(), + e); + } + + if (node.getModifiedPomXMLEventReader().isModified()) { + if (generateBackupPoms) { + File backupFile = new File( + node.getModel().getPomFile().getParentFile(), + node.getModel().getPomFile().getName() + ".versionsBackup"); + if (!backupFile.exists()) { + getLog().debug("Backing up " + node.getModel().getPomFile() + " to " + backupFile); + try { + FileUtils.copyFile(node.getModel().getPomFile(), backupFile); + } catch (IOException e) { + throw new MojoFailureException( + "Error backing up the " + node.getModel().getPomFile(), e); + } + } else { + getLog().debug("Leaving existing backup " + backupFile + " unmodified"); + } + } else { + getLog().debug("Skipping the generation of a backup file"); + } + try { + writeFile( + node.getModel().getPomFile(), + node.getModifiedPomXMLEventReader().asStringBuilder()); + } catch (IOException e) { + throw new MojoFailureException( + "Unable to write the changed file " + node.getModel().getPomFile(), e); } + } + + try { + saveChangeRecorderResults(); } catch (IOException e) { - throw new MojoExecutionException(e.getMessage(), e); + getLog().warn( + "Cannot save the change recorder result for file " + + node.getModel().getPomFile(), + e); } + + return node.getModifiedPomXMLEventReader().isModified(); } + private static List getDependencies(Model model) { + List dependencies = ofNullable(model.getDependencies()).orElse(new ArrayList<>()); + dependencies.addAll(ofNullable(model.getProfiles()) + .flatMap(profiles -> profiles.stream() + .map(ModelBase::getDependencies) + .reduce((l1, l2) -> { + l1.addAll(l2); + return l1; + })) + .orElse(emptyList())); + return dependencies; + } + + /** + *

Will process the given module tree node, updating the {@link ModifiedPomXMLEventReader} associated with the + * node if it finds a dependency matching the filter that needs to be changed or, if {@link #processProperties} + * is {@code true}, a property value that can be updated.

+ *

The method will use the set passed as the {@code backlog} argument to store the properties which it needs + * to update, but which were not found in the current tree. These properties need to be carried over to the parent + * node for processing.

+ *

Similarly, method will use the map passed as the {@code propertyConflicts} argument to store properties + * which are associated with dependencies which do not fit the filter and thus may not be changed. This is then + * used for conflict detection if a dependency to be changed used one of these properties. Such a change + * is not allowed and must be reported instead.

+ * + * @param node model tree node to process + * @param dependencies collection of dependencies to process (can be taken from dependency management, + * parent, or dependencies) + * @param changeKind {@link ChangeRecord.ChangeKind} instance for the change recorder + * @param propertyBacklog a {@link Set} instance used to store dependencies to be updated, but which were not found + * in the current subtree. These properties need to be carried over to the parent node for + * processing. + * @param propertyConflicts an {@link Map} instance to store properties + * which are associated with dependencies which do not fit the filter and thus may not + * be changed. This is then used for conflict detection if a dependency to be changed + * used one of these properties. Such a change is not allowed and must be reported instead. + * @throws MojoExecutionException thrown if a version may not be changed + * @throws XMLStreamException thrown if a {@link ModifiedPomXMLEventReader} can't be updated + * @throws VersionRetrievalException thrown if dependency versions cannot be retrieved + */ private void useDepVersion( - ModifiedPomXMLEventReader pom, Collection dependencies, ChangeRecord.ChangeKind changeKind) + ModelNode node, + Collection dependencies, + ChangeRecord.ChangeKind changeKind, + Set propertyBacklog, + Map> propertyConflicts) throws MojoExecutionException, XMLStreamException, VersionRetrievalException { + // an additional pass is necessary to collect conflicts if processProperties is enabled + if (processProperties) { + dependencies.stream() + .filter(dep -> { + try { + return !isIncluded(toArtifact(dep)); + } catch (MojoExecutionException e) { + throw new RuntimeException(e); + } + }) + .forEach(dep -> + // if a dependency that is _not_ to be changed is set using a property, register that + // property + // in propertyConflicts; these are the properties that must not be changed + // the list in the value is the list of dependencies that use the said property + PomHelper.extractExpression(dep.getVersion()) + .ifPresent(p -> propertyConflicts.compute(p, (k, v) -> ofNullable(v) + .map(set -> { + set.add(dep); + return set; + }) + .orElseGet(() -> { + Set set = new TreeSet<>(DependencyComparator.INSTANCE); + set.add(dep); + return set; + })))); + } + + // 2nd pass: check dependencies for (Dependency dep : dependencies) { if (isExcludeReactor() && isProducedByReactor(dep)) { - getLog().info("Ignoring reactor dependency: " + toString(dep)); + getLog().info("Ignoring a reactor dependency: " + toString(dep)); continue; } - if (isHandledByProperty(dep)) { - getLog().debug("Ignoring dependency with property as version: " + toString(dep)); + Optional propertyName = PomHelper.extractExpression(dep.getVersion()); + if (propertyName.isPresent() && !processProperties) { + getLog().info("Ignoring a dependency with the version set using a property: " + toString(dep)); continue; } - Artifact artifact = this.toArtifact(dep); - + Artifact artifact = toArtifact(dep); if (isIncluded(artifact)) { if (!forceVersion) { - ArtifactVersions versions = getHelper().lookupArtifactVersions(artifact, false); - - if (!versions.containsVersion(depVersion)) { + if (!getHelper().lookupArtifactVersions(artifact, false).containsVersion(depVersion)) { throw new MojoExecutionException(String.format( "Version %s is not available for artifact %s:%s", depVersion, artifact.getGroupId(), artifact.getArtifactId())); } } - updateDependencyVersion(pom, dep, depVersion, changeKind); + if (!propertyName.isPresent()) { + updateDependencyVersion(node.getModifiedPomXMLEventReader(), dep, depVersion, changeKind); + } else { + // propertyName is present + ofNullable(propertyConflicts.get(propertyName.get())) + .map(conflict -> { + getLog().warn("Cannot update property ${" + propertyName.get() + "}: " + + "controls more than one dependency: " + + conflict.stream() + .map(Dependency::getArtifactId) + .collect(Collectors.joining(", "))); + return false; + }) + .orElseGet(() -> { + if (!updatePropertyValue(node, propertyName.get())) { + propertyBacklog.add(propertyName.get()); + } else { + if (getLog().isDebugEnabled()) { + getLog().debug(String.format( + "Updated the %s property value to %s.", dep.getVersion(), depVersion)); + } + } + return true; + }); + } } } + + // third pass: if a property is defined at this node, it is not going to conflict with anything from parent + propertyConflicts.keySet().removeIf(key -> ofNullable(node.getModel().getProperties()) + .filter(p -> p.containsKey(key)) + .isPresent()); + propertyConflicts.keySet().removeIf(key -> ofNullable(node.getModel().getProfiles()) + .map(list -> list.stream().anyMatch(p -> ofNullable(p.getProperties()) + .filter(prop -> prop.containsKey(key)) + .isPresent())) + .orElse(false)); + } + + private boolean updatePropertyValue(ModelNode node, String property) { + return ofNullable(node.getModel().getProperties()) + .filter(p -> p.containsKey(property)) + .map(ignored -> { + try { + return PomHelper.setPropertyVersion( + node.getModifiedPomXMLEventReader(), null, property, depVersion); + } catch (XMLStreamException e) { + throw new RuntimeException(e); + } + }) + .orElse(false) + | ofNullable(node.getModel().getProfiles()) + .flatMap(profiles -> profiles.stream() + .map(profile -> ofNullable(profile.getProperties()) + .filter(p -> p.containsKey(property)) + .map(ignored -> { + try { + return PomHelper.setPropertyVersion( + node.getModifiedPomXMLEventReader(), + profile.getId(), + property, + depVersion); + } catch (XMLStreamException e) { + throw new RuntimeException(e); + } + }) + .orElse(false)) + .reduce(Boolean::logicalOr)) + .orElse(false); } } diff --git a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/UseDepVersionMojoTest.java b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/UseDepVersionMojoTest.java index 31c04aa7c..f1aaf8bf0 100644 --- a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/UseDepVersionMojoTest.java +++ b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/UseDepVersionMojoTest.java @@ -19,14 +19,31 @@ * under the License. */ -import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Collections; +import org.apache.maven.plugin.logging.Log; +import org.apache.maven.plugin.logging.SystemStreamLog; import org.apache.maven.plugin.testing.AbstractMojoTestCase; import org.apache.maven.plugin.testing.MojoRule; +import org.codehaus.mojo.versions.utils.TestChangeRecorder; +import org.codehaus.mojo.versions.utils.TestUtils; +import org.junit.After; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import static org.codehaus.mojo.versions.utils.MockUtils.mockAetherRepositorySystem; +import static org.codehaus.mojo.versions.utils.MockUtils.mockRepositorySystem; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.empty; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; + /** * Basic tests for {@linkplain UseDepVersionMojo}. * @@ -36,16 +53,275 @@ public class UseDepVersionMojoTest extends AbstractMojoTestCase { @Rule public MojoRule mojoRule = new MojoRule(this); + private Path tempDir; + + @Before + public void setUp() throws Exception { + super.setUp(); + tempDir = TestUtils.createTempDir("use-dep-version"); + } + + @After + public void tearDown() throws Exception { + try { + TestUtils.tearDownTempDir(tempDir); + } finally { + super.tearDown(); + } + } + @Test public void testIssue673() throws Exception { - UseDepVersionMojo mojo = (UseDepVersionMojo) mojoRule.lookupConfiguredMojo( - new File("target/test-classes/org/codehaus/mojo/use-dep-version/issue-637"), "use-dep-version"); - setVariableValueToObject(mojo, "processDependencies", true); - setVariableValueToObject(mojo, "processDependencyManagement", true); - setVariableValueToObject(mojo, "excludeReactor", true); + TestUtils.copyDir(Paths.get("src/test/resources/org/codehaus/mojo/use-dep-version/issue-637"), tempDir); + UseDepVersionMojo mojo = (UseDepVersionMojo) mojoRule.lookupConfiguredMojo(tempDir.toFile(), "use-dep-version"); setVariableValueToObject(mojo, "serverId", "serverId"); setVariableValueToObject(mojo, "reactorProjects", Collections.singletonList(mojo.getProject())); mojo.execute(); } + + /** + * Tests a simple case with a single property: the property value needs to be changed. + * @throws Exception thrown if something goes not according to plan + */ + @Test + public void testPropertiesSimple() throws Exception { + Log logger = new SystemStreamLog() { + @Override + public boolean isDebugEnabled() { + return true; + } + }; + TestUtils.copyDir(Paths.get("src/test/resources/org/codehaus/mojo/use-dep-version/properties/simple"), tempDir); + TestChangeRecorder changeRecorder = new TestChangeRecorder(); + UseDepVersionMojo mojo = (UseDepVersionMojo) mojoRule.lookupConfiguredMojo(tempDir.toFile(), "use-dep-version"); + setVariableValueToObject(mojo, "reactorProjects", Collections.singletonList(mojo.getProject())); + setVariableValueToObject(mojo, "repositorySystem", mockRepositorySystem()); + setVariableValueToObject(mojo, "aetherRepositorySystem", mockAetherRepositorySystem()); + setVariableValueToObject(mojo, "log", logger); + + mojo.execute(); + + String pom = String.join("", Files.readAllLines(tempDir.resolve("pom.xml"))); + assertThat(pom, containsString("${revision}")); + assertThat(pom, containsString("2.0.0")); + } + + /** + * The same as {@link #testPropertiesSimple()}, but with profiles. + * @throws Exception thrown if something goes not according to plan + */ + @Test + public void testPropertiesSimpleProfiles() throws Exception { + Log logger = new SystemStreamLog() { + @Override + public boolean isDebugEnabled() { + return true; + } + }; + TestUtils.copyDir( + Paths.get("src/test/resources/org/codehaus/mojo/use-dep-version/properties/simple-profiles"), tempDir); + TestChangeRecorder changeRecorder = new TestChangeRecorder(); + UseDepVersionMojo mojo = (UseDepVersionMojo) mojoRule.lookupConfiguredMojo(tempDir.toFile(), "use-dep-version"); + setVariableValueToObject(mojo, "reactorProjects", Collections.singletonList(mojo.getProject())); + setVariableValueToObject(mojo, "repositorySystem", mockRepositorySystem()); + setVariableValueToObject(mojo, "aetherRepositorySystem", mockAetherRepositorySystem()); + setVariableValueToObject(mojo, "log", logger); + + mojo.execute(); + + String pom = String.join("", Files.readAllLines(tempDir.resolve("pom.xml"))); + assertThat(pom, containsString("${revision}")); + assertThat(pom, containsString("2.0.0")); + } + + /** + * Tests a case with a single property used for more than one dependency, of which only one is to be changed: + * the property value must remain unchanged, and a warning must be logged. + * @throws Exception thrown if something goes not according to plan + */ + @Test + public void testPropertiesConflict() throws Exception { + Log logger = mock(Log.class); + StringBuilder warnLog = new StringBuilder(); + doAnswer(i -> warnLog.append(i.getArgument(0).toString())).when(logger).warn(anyString()); + TestChangeRecorder changeRecorder = new TestChangeRecorder(); + + TestUtils.copyDir( + Paths.get("src/test/resources/org/codehaus/mojo/use-dep-version/properties/conflict"), tempDir); + UseDepVersionMojo mojo = (UseDepVersionMojo) mojoRule.lookupConfiguredMojo(tempDir.toFile(), "use-dep-version"); + setVariableValueToObject(mojo, "reactorProjects", Collections.singletonList(mojo.getProject())); + setVariableValueToObject(mojo, "repositorySystem", mockRepositorySystem()); + setVariableValueToObject(mojo, "aetherRepositorySystem", mockAetherRepositorySystem()); + setVariableValueToObject(mojo, "changeRecorders", changeRecorder.asTestMap()); + setVariableValueToObject(mojo, "log", logger); + + mojo.execute(); + + assertThat(changeRecorder.getChanges(), empty()); + assertThat( + warnLog.toString(), + containsString("Cannot update property ${revision}: controls more than one dependency: artifactB")); + } + + /** + * Tests a case with a single property used for more than one dependency, of which only one is to be changed: + * however, the other dependency (not to be changed) uses the redefined value of the property. + * In this case, the change should take place in the child, but not in the parent. + * @throws Exception thrown if something goes not according to plan + */ + @Test + public void testPropertiesConflictRedefinition() throws Exception { + TestUtils.copyDir( + Paths.get("src/test/resources/org/codehaus/mojo/use-dep-version/properties/conflict-redefinition"), + tempDir); + UseDepVersionMojo mojo = (UseDepVersionMojo) mojoRule.lookupConfiguredMojo(tempDir.toFile(), "use-dep-version"); + setVariableValueToObject(mojo, "reactorProjects", Collections.singletonList(mojo.getProject())); + setVariableValueToObject(mojo, "repositorySystem", mockRepositorySystem()); + setVariableValueToObject(mojo, "aetherRepositorySystem", mockAetherRepositorySystem()); + + mojo.execute(); + + String child = String.join("", Files.readAllLines(tempDir.resolve("child/pom.xml"))); + String parent = String.join("", Files.readAllLines(tempDir.resolve("pom.xml"))); + assertThat(child, containsString("${revision}")); + assertThat(parent, containsString("${revision}")); + assertThat(child, containsString("2.0.0")); + assertThat(parent, containsString("1.0.0-SNAPSHOT")); + } + + /** + * Tests a case with a single property used for more than one dependency, of which only one is to be changed: + * the dependency to be changed is in the parent, and both the child and the parent redefine the same property. + * Because the property is redefined at the child level, the child is immune to property changes, hence + * the substitution must take place. + * @throws Exception thrown if something goes not according to plan + */ + @Test + public void testPropertiesConflictCancellation() throws Exception { + TestUtils.copyDir( + Paths.get("src/test/resources/org/codehaus/mojo/use-dep-version/properties/conflict-cancellation"), + tempDir); + UseDepVersionMojo mojo = (UseDepVersionMojo) mojoRule.lookupConfiguredMojo(tempDir.toFile(), "use-dep-version"); + setVariableValueToObject(mojo, "reactorProjects", Collections.singletonList(mojo.getProject())); + setVariableValueToObject(mojo, "repositorySystem", mockRepositorySystem()); + setVariableValueToObject(mojo, "aetherRepositorySystem", mockAetherRepositorySystem()); + + mojo.execute(); + + String child = String.join("", Files.readAllLines(tempDir.resolve("child/pom.xml"))); + String parent = String.join("", Files.readAllLines(tempDir.resolve("pom.xml"))); + assertThat(child, containsString("${revision}")); + assertThat(parent, containsString("${revision}")); + assertThat(parent, containsString("2.0.0")); + assertThat(child, containsString("1.0.1")); + } + + /** + * The same as {@link #testPropertiesConflictCancellation()}, but working on profiles. + * @throws Exception thrown if something goes not according to plan + */ + @Test + public void testPropertiesConflictCancellationProfiles() throws Exception { + TestUtils.copyDir( + Paths.get("src/test/resources/org/codehaus/mojo/use-dep-version/properties/" + + "conflict-cancellation-profiles"), + tempDir); + UseDepVersionMojo mojo = (UseDepVersionMojo) mojoRule.lookupConfiguredMojo(tempDir.toFile(), "use-dep-version"); + setVariableValueToObject(mojo, "reactorProjects", Collections.singletonList(mojo.getProject())); + setVariableValueToObject(mojo, "repositorySystem", mockRepositorySystem()); + setVariableValueToObject(mojo, "aetherRepositorySystem", mockAetherRepositorySystem()); + + mojo.execute(); + + String child = String.join("", Files.readAllLines(tempDir.resolve("child/pom.xml"))); + String parent = String.join("", Files.readAllLines(tempDir.resolve("pom.xml"))); + assertThat(child, containsString("${revision}")); + assertThat(parent, containsString("${revision}")); + assertThat(parent, containsString("2.0.0")); + assertThat(child, containsString("1.0.1")); + } + + /** + * Tests a case with a single property defined in the parent, and used in the child: the property value in + * the parent needs to be updated. + * @throws Exception thrown if something goes not according to plan + */ + @Test + public void testPropertiesChildParent() throws Exception { + Log logger = new SystemStreamLog() { + @Override + public boolean isDebugEnabled() { + return true; + } + }; + TestUtils.copyDir( + Paths.get("src/test/resources/org/codehaus/mojo/use-dep-version/properties/child-parent"), tempDir); + UseDepVersionMojo mojo = (UseDepVersionMojo) mojoRule.lookupConfiguredMojo(tempDir.toFile(), "use-dep-version"); + setVariableValueToObject(mojo, "reactorProjects", Collections.singletonList(mojo.getProject())); + setVariableValueToObject(mojo, "repositorySystem", mockRepositorySystem()); + setVariableValueToObject(mojo, "aetherRepositorySystem", mockAetherRepositorySystem()); + setVariableValueToObject(mojo, "log", logger); + + mojo.execute(); + + String child = String.join("", Files.readAllLines(tempDir.resolve("child/pom.xml"))); + String parent = String.join("", Files.readAllLines(tempDir.resolve("pom.xml"))); + assertThat(child, containsString("${revision}")); + assertThat(parent, containsString("2.0.0")); + } + + /** + * Tests a case with a single property defined in the parent and then redefined in the child: the property + * must be redefined in the child and remain the same in the parent. + * @throws Exception thrown if something goes not according to plan + */ + @Test + public void testPropertiesChildParentRedefinition() throws Exception { + TestUtils.copyDir( + Paths.get("src/test/resources/org/codehaus/mojo/use-dep-version/properties/child-parent-redefinition"), + tempDir); + UseDepVersionMojo mojo = (UseDepVersionMojo) mojoRule.lookupConfiguredMojo(tempDir.toFile(), "use-dep-version"); + setVariableValueToObject(mojo, "reactorProjects", Collections.singletonList(mojo.getProject())); + setVariableValueToObject(mojo, "repositorySystem", mockRepositorySystem()); + setVariableValueToObject(mojo, "aetherRepositorySystem", mockAetherRepositorySystem()); + + mojo.execute(); + + String child = String.join("", Files.readAllLines(tempDir.resolve("child/pom.xml"))); + String parent = String.join("", Files.readAllLines(tempDir.resolve("pom.xml"))); + assertThat(child, containsString("${revision}")); + assertThat(parent, containsString("1.0.0-SNAPSHOT")); + assertThat(child, containsString("2.0.0")); + } + + /** + * Tests a case with a single property defined in the parent: a warning must be logged and no files must + * be changed. + * @throws Exception thrown if something goes not according to plan + */ + @Test + public void testPropertyFromParent() throws Exception { + Log logger = mock(Log.class); + StringBuilder log = new StringBuilder(); + doAnswer(i -> log.append("[WARN] ").append(i.getArgument(0).toString())) + .when(logger) + .warn(anyString()); + TestChangeRecorder changeRecorder = new TestChangeRecorder(); + + TestUtils.copyDir( + Paths.get("src/test/resources/org/codehaus/mojo/use-dep-version/properties/child-parent"), tempDir); + UseDepVersionMojo mojo = (UseDepVersionMojo) + mojoRule.lookupConfiguredMojo(tempDir.resolve("child").toFile(), "use-dep-version"); + setVariableValueToObject(mojo, "reactorProjects", Collections.singletonList(mojo.getProject())); + setVariableValueToObject(mojo, "repositorySystem", mockRepositorySystem()); + setVariableValueToObject(mojo, "aetherRepositorySystem", mockAetherRepositorySystem()); + setVariableValueToObject(mojo, "changeRecorders", changeRecorder.asTestMap()); + setVariableValueToObject(mojo, "log", logger); + + mojo.execute(); + + assertThat(changeRecorder.getChanges(), empty()); + assertThat(log.toString(), containsString("[WARN] Not updating property ${revision}: defined in parent")); + } } diff --git a/versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/child-parent-redefinition/child/pom.xml b/versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/child-parent-redefinition/child/pom.xml new file mode 100644 index 000000000..417a2aec3 --- /dev/null +++ b/versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/child-parent-redefinition/child/pom.xml @@ -0,0 +1,26 @@ + + + 4.0.0 + + + test-group + parent + 1.0.0 + + + child + 1.0.0 + + + 1.0.1 + + + + + test-group + artifactA + ${revision} + + + + diff --git a/versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/child-parent-redefinition/pom.xml b/versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/child-parent-redefinition/pom.xml new file mode 100644 index 000000000..8cfde5786 --- /dev/null +++ b/versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/child-parent-redefinition/pom.xml @@ -0,0 +1,33 @@ + + + 4.0.0 + + test-group + parent + 1.0.0 + pom + + + 1.0.0-SNAPSHOT + + + + child + + + + + + org.codehaus.mojo + versions-maven-plugin + + test-group + 2.0.0 + false + true + + + + + + diff --git a/versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/child-parent/child/pom.xml b/versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/child-parent/child/pom.xml new file mode 100644 index 000000000..4e5aa1790 --- /dev/null +++ b/versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/child-parent/child/pom.xml @@ -0,0 +1,22 @@ + + + 4.0.0 + + + test-group + parent + 1.0.0 + + + child + 1.0.0 + + + + test-group + artifactA + ${revision} + + + + diff --git a/versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/child-parent/pom.xml b/versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/child-parent/pom.xml new file mode 100644 index 000000000..d137a888b --- /dev/null +++ b/versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/child-parent/pom.xml @@ -0,0 +1,33 @@ + + + 4.0.0 + + test-group + parent + 1.0.0 + pom + + + 1.0.0 + + + + child + + + + + + org.codehaus.mojo + versions-maven-plugin + + test-group + 2.0.0 + false + true + + + + + + diff --git a/versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/conflict-cancellation-profiles/child/pom.xml b/versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/conflict-cancellation-profiles/child/pom.xml new file mode 100644 index 000000000..417a2aec3 --- /dev/null +++ b/versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/conflict-cancellation-profiles/child/pom.xml @@ -0,0 +1,26 @@ + + + 4.0.0 + + + test-group + parent + 1.0.0 + + + child + 1.0.0 + + + 1.0.1 + + + + + test-group + artifactA + ${revision} + + + + diff --git a/versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/conflict-cancellation-profiles/pom.xml b/versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/conflict-cancellation-profiles/pom.xml new file mode 100644 index 000000000..87da09859 --- /dev/null +++ b/versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/conflict-cancellation-profiles/pom.xml @@ -0,0 +1,50 @@ + + + 4.0.0 + + test-group + parent + 1.0.0 + pom + + + child + + + + + test-profile + + true + + + 1.0.0-SNAPSHOT + + + + test-group + artifactB + ${revision} + + + + + + + + + org.codehaus.mojo + versions-maven-plugin + + test-group:artifactB + 2.0.0 + false + true + + true + + + + + + diff --git a/versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/conflict-cancellation/child/pom.xml b/versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/conflict-cancellation/child/pom.xml new file mode 100644 index 000000000..417a2aec3 --- /dev/null +++ b/versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/conflict-cancellation/child/pom.xml @@ -0,0 +1,26 @@ + + + 4.0.0 + + + test-group + parent + 1.0.0 + + + child + 1.0.0 + + + 1.0.1 + + + + + test-group + artifactA + ${revision} + + + + diff --git a/versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/conflict-cancellation/pom.xml b/versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/conflict-cancellation/pom.xml new file mode 100644 index 000000000..279fe3acb --- /dev/null +++ b/versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/conflict-cancellation/pom.xml @@ -0,0 +1,43 @@ + + + 4.0.0 + + test-group + parent + 1.0.0 + pom + + + 1.0.0-SNAPSHOT + + + + child + + + + + test-group + artifactB + ${revision} + + + + + + + org.codehaus.mojo + versions-maven-plugin + + test-group:artifactB + 2.0.0 + false + true + + true + + + + + + diff --git a/versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/conflict-redefinition/child/pom.xml b/versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/conflict-redefinition/child/pom.xml new file mode 100644 index 000000000..417a2aec3 --- /dev/null +++ b/versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/conflict-redefinition/child/pom.xml @@ -0,0 +1,26 @@ + + + 4.0.0 + + + test-group + parent + 1.0.0 + + + child + 1.0.0 + + + 1.0.1 + + + + + test-group + artifactA + ${revision} + + + + diff --git a/versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/conflict-redefinition/pom.xml b/versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/conflict-redefinition/pom.xml new file mode 100644 index 000000000..bdfc03ac6 --- /dev/null +++ b/versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/conflict-redefinition/pom.xml @@ -0,0 +1,43 @@ + + + 4.0.0 + + test-group + parent + 1.0.0 + pom + + + 1.0.0-SNAPSHOT + + + + child + + + + + test-group + artifactB + ${revision} + + + + + + + org.codehaus.mojo + versions-maven-plugin + + test-group:artifactA + 2.0.0 + false + true + + true + + + + + + diff --git a/versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/conflict/pom.xml b/versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/conflict/pom.xml new file mode 100644 index 000000000..7ebdd91f8 --- /dev/null +++ b/versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/conflict/pom.xml @@ -0,0 +1,43 @@ + + + 4.0.0 + + test-group + test-artifact + DEVELOP-SNAPSHOT + + + 1.0.0-SNAPSHOT + + + + + test-group + artifactA + ${revision} + + + test-group + artifactB + ${revision} + + + + + + + org.codehaus.mojo + versions-maven-plugin + + serverId + test-group:artifactA + 2.0.0 + true + + + + + + diff --git a/versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/simple-profiles/pom.xml b/versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/simple-profiles/pom.xml new file mode 100644 index 000000000..0e1bd4e6e --- /dev/null +++ b/versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/simple-profiles/pom.xml @@ -0,0 +1,47 @@ + + + 4.0.0 + + test-group + test-artifact + DEVELOP-SNAPSHOT + + + + test-profile + + true + + + 1.0.0-SNAPSHOT + + + + + default-group + artifactA + ${revision} + + + + + + + + + + org.codehaus.mojo + versions-maven-plugin + + serverId + default-group + 2.0.0 + true + + + + + + diff --git a/versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/simple/pom.xml b/versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/simple/pom.xml new file mode 100644 index 000000000..ebc4594e4 --- /dev/null +++ b/versions-maven-plugin/src/test/resources/org/codehaus/mojo/use-dep-version/properties/simple/pom.xml @@ -0,0 +1,38 @@ + + + 4.0.0 + + test-group + test-artifact + DEVELOP-SNAPSHOT + + + 1.0.0-SNAPSHOT + + + + + default-group + artifactA + ${revision} + + + + + + + org.codehaus.mojo + versions-maven-plugin + + serverId + default-group + 2.0.0 + true + + + + + +