diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/AddDependency.java b/rewrite-maven/src/main/java/org/openrewrite/maven/AddDependency.java index c8257bc8752..fdb8b6bdb39 100644 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/AddDependency.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/AddDependency.java @@ -29,7 +29,10 @@ import org.openrewrite.semver.Semver; import org.openrewrite.xml.tree.Xml; -import java.util.*; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; import java.util.regex.Pattern; import static java.util.Objects.requireNonNull; @@ -180,17 +183,17 @@ public Tree visit(@Nullable Tree tree, ExecutionContext ctx) { acc.usingType = true; JavaProject javaProject = sourceFile.getMarkers().findFirst(JavaProject.class).orElse(null); JavaSourceSet javaSourceSet = sourceFile.getMarkers().findFirst(JavaSourceSet.class).orElse(null); - if(javaProject != null && javaSourceSet != null) { + if (javaProject != null && javaSourceSet != null) { acc.scopeByProject.compute(javaProject, (jp, scope) -> "compile".equals(scope) ? scope /* a `compile` scope dependency will also be available in test source set */ : "test".equals(javaSourceSet.getName()) ? "test" : "compile" ); } } - } else if(tree instanceof Xml.Document) { + } else if (tree instanceof Xml.Document) { Xml.Document doc = (Xml.Document) tree; MavenResolutionResult mrr = doc.getMarkers().findFirst(MavenResolutionResult.class).orElse(null); - if(mrr == null) { + if (mrr == null) { return sourceFile; } acc.pomsDefinedInCurrentRepository.add(mrr.getPom().getGav()); @@ -235,7 +238,14 @@ public Xml visitDocument(Xml.Document document, ExecutionContext ctx) { } } - if(onlyIfUsing == null && getResolutionResult().getParent() != null && acc.pomsDefinedInCurrentRepository.contains(getResolutionResult().getParent().getPom().getGav())) { + if (onlyIfUsing == null && getResolutionResult().getParent() != null && acc.pomsDefinedInCurrentRepository.contains(getResolutionResult().getParent().getPom().getGav())) { + return maven; + } + + if (!getResolutionResult().getPom().getModules().isEmpty() + && (getResolutionResult().getModules().isEmpty() + || getResolutionResult().getModules().stream().map(MavenResolutionResult::getPom).map(ResolvedPom::getGav).map(ResolvedGroupArtifactVersion::getArtifactId).anyMatch(art -> getResolutionResult().getPom().getModules().contains(art)) + )) { return maven; } diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/internal/RawPom.java b/rewrite-maven/src/main/java/org/openrewrite/maven/internal/RawPom.java index 4ae0fc5b3da..bffdaa2c767 100755 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/internal/RawPom.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/internal/RawPom.java @@ -118,6 +118,9 @@ public class RawPom { @Nullable Profiles profiles; + @Nullable + Modules modules; + public static RawPom parse(InputStream inputStream, @Nullable String snapshotVersion) { try { RawPom pom = MavenXmlMapper.readMapper().readValue(inputStream, RawPom.class); @@ -217,6 +220,19 @@ public Profiles(@JacksonXmlProperty(localName = "profile") List profile } } + @Getter + public static class Modules { + private final List modules; + + public Modules() { + this.modules = emptyList(); + } + + public Modules(@JacksonXmlProperty(localName = "module") List modules) { + this.modules = modules; + } + } + @FieldDefaults(level = AccessLevel.PRIVATE) @Data public static class Build { @@ -346,9 +362,9 @@ public static class Profile { } public @Nullable String getVersion() { - if(version == null) { - if(currentVersion == null) { - if(parent == null) { + if (version == null) { + if (currentVersion == null) { + if (parent == null) { return null; } else { return parent.getVersion(); @@ -382,8 +398,9 @@ public Pom toPom(@Nullable Path inputPath, @Nullable MavenRepository repo) { .packaging(packaging) .properties(getProperties() == null ? emptyMap() : getProperties()) .licenses(mapLicenses(getLicenses())) - .profiles(mapProfiles(getProfiles())); - if(StringUtils.isBlank(pomVersion)) { + .profiles(mapProfiles(getProfiles())) + .modules(getModules() == null ? emptyList() : getModules().getModules()); + if (StringUtils.isBlank(pomVersion)) { builder.dependencies(mapRequestedDependencies(getDependencies())) .dependencyManagement(mapDependencyManagement(getDependencyManagement())) .repositories(mapRepositories(getRepositories())) diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/tree/MavenResolutionResult.java b/rewrite-maven/src/main/java/org/openrewrite/maven/tree/MavenResolutionResult.java index 2530580ec43..38b290a79fc 100644 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/tree/MavenResolutionResult.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/tree/MavenResolutionResult.java @@ -209,6 +209,22 @@ public boolean parentPomIsProjectPom() { .anyMatch(gav -> gav.equals(parentGav)); } + /** + * Often recipes operating on multi-module projects will prefer to make changes to the parent pom rather than in multiple child poms. + * But if the parent isn't in the same repository as the child, the recipe would need to be applied to the child poms. + * + * @return true when the parent pom of the current pom is present in the same repository as the current pom + */ + public boolean isAggregatingPom() { + if (getParent() == null) { + return false; + } + ResolvedGroupArtifactVersion parentGav = getParent().getPom().getGav(); + return getProjectPoms().values().stream() + .map(Pom::getGav) + .anyMatch(gav -> gav.equals(parentGav)); + } + private Map getProjectPomsRecursive(Map projectPoms) { projectPoms.put(requireNonNull(pom.getRequested().getSourcePath()), pom.getRequested()); if (parent != null) { diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/tree/Pom.java b/rewrite-maven/src/main/java/org/openrewrite/maven/tree/Pom.java index e5af8d7bb32..7379e9df216 100644 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/tree/Pom.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/tree/Pom.java @@ -115,6 +115,9 @@ public static int getModelVersion() { @Builder.Default List pluginManagement = emptyList(); + @Builder.Default + List modules = emptyList(); + public String getGroupId() { return gav.getGroupId(); } @@ -184,7 +187,8 @@ public ResolvedPom resolve(Iterable activeProfiles, repositories, dependencies, plugins, - pluginManagement) + pluginManagement, + modules) .resolve(ctx, downloader); } diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/tree/ResolvedPom.java b/rewrite-maven/src/main/java/org/openrewrite/maven/tree/ResolvedPom.java index e00559f9eb7..a87b682b855 100644 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/tree/ResolvedPom.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/tree/ResolvedPom.java @@ -69,11 +69,11 @@ public class ResolvedPom { Iterable activeProfiles; public ResolvedPom(Pom requested, Iterable activeProfiles) { - this(requested, activeProfiles, emptyMap(), emptyList(), null, emptyList(), emptyList(), emptyList(), emptyList()); + this(requested, activeProfiles, emptyMap(), emptyList(), null, emptyList(), emptyList(), emptyList(), emptyList(), emptyList()); } @JsonCreator - ResolvedPom(Pom requested, Iterable activeProfiles, Map properties, List dependencyManagement, @Nullable List initialRepositories, List repositories, List requestedDependencies, List plugins, List pluginManagement) { + ResolvedPom(Pom requested, Iterable activeProfiles, Map properties, List dependencyManagement, @Nullable List initialRepositories, List repositories, List requestedDependencies, List plugins, List pluginManagement, List modules) { this.requested = requested; this.activeProfiles = activeProfiles; this.properties = properties; @@ -83,6 +83,7 @@ public ResolvedPom(Pom requested, Iterable activeProfiles) { this.requestedDependencies = requestedDependencies; this.plugins = plugins; this.pluginManagement = pluginManagement; + this.modules = modules; } @NonFinal @@ -113,6 +114,10 @@ public ResolvedPom(Pom requested, Iterable activeProfiles) { @Builder.Default List pluginManagement = emptyList(); + @NonFinal + @Builder.Default + List modules = emptyList(); + /** * Deduplicate dependencies and dependency management dependencies @@ -172,6 +177,7 @@ public ResolvedPom resolve(ExecutionContext ctx, MavenPomDownloader downloader) emptyList(), emptyList(), emptyList(), + emptyList(), emptyList() ).resolver(ctx, downloader).resolve(); @@ -926,7 +932,7 @@ public List resolveDependencies(Scope scope, Map @@ -1464,6 +1461,167 @@ class Foo {} ); } + @Test + void addDependencyToParentPomWhenAggregatingPomIsNotParent() { + rewriteRun( + spec -> spec.recipe(new AddDependency( + "org.hamcrest", + "hamcrest-junit", + "2.0.0.0", + null, + "test", + null, + null, + null, + null, + null, + null, + true)), + mavenProject("my-app-aggregate", + pomXml( + """ + + com.mycompany.app + my-app-aggregate + 1 + + project-parent + project-child + + + """)), + mavenProject("project-parent", + pomXml( + """ + + com.mycompany.app + my-app-parent + 1 + + org.springframework.boot + spring-boot-starter-parent + 3.1.5 + + + """, + """ + + com.mycompany.app + my-app-parent + 1 + + org.springframework.boot + spring-boot-starter-parent + 3.1.5 + + + + org.hamcrest + hamcrest-junit + 2.0.0.0 + test + + + + """) + ), + mavenProject("my-app-child", + pomXml( + """ + + com.mycompany.app + my-app-child + 1 + + com.mycompany.app + my-app-parent + 1 + + + """)) + ); + } + + @Test + void addDependencyToAggregatingPomWhenAggregatingPomIsParent() { + rewriteRun( + spec -> spec.recipe(new AddDependency( + "org.hamcrest", + "hamcrest-junit", + "2.0.0.0", + null, + "test", + null, + null, + null, + null, + null, + null, + true)), + mavenProject("my-app-aggregate", + pomXml( + """ + + com.mycompany.app + my-app-aggregate + 1 + + project-parent + project-child + + + """, + """ + + com.mycompany.app + my-app-aggregate + 1 + + project-parent + project-child + + + + org.hamcrest + hamcrest-junit + 2.0.0.0 + test + + + + """)), + mavenProject("project-parent", + pomXml( + """ + + com.mycompany.app + my-app-parent + 1 + + org.springframework.boot + spring-boot-starter-parent + 3.1.5 + + + """) + ), + mavenProject("my-app-child", + pomXml( + """ + + com.mycompany.app + my-app-child + 1 + + com.mycompany.app + my-app-aggregate + 1 + + + """)) + ); + } + private AddDependency addDependency(@SuppressWarnings("SameParameterValue") String gav) { return addDependency(gav, null, null, null); }