diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/RemoveRedundantDependencyVersions.java b/rewrite-maven/src/main/java/org/openrewrite/maven/RemoveRedundantDependencyVersions.java index 7fc17d1321..628b6272d8 100644 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/RemoveRedundantDependencyVersions.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/RemoveRedundantDependencyVersions.java @@ -18,8 +18,10 @@ import com.fasterxml.jackson.annotation.JsonCreator; import lombok.EqualsAndHashCode; import lombok.Value; +import lombok.With; import org.jspecify.annotations.Nullable; import org.openrewrite.*; +import org.openrewrite.marker.Marker; import org.openrewrite.internal.ListUtils; import org.openrewrite.internal.StringUtils; import org.openrewrite.maven.internal.MavenPomDownloader; @@ -172,6 +174,9 @@ public Xml.Document visitDocument(Xml.Document document, ExecutionContext ctx) { @Override public Xml.@Nullable Tag visitTag(Xml.Tag tag, ExecutionContext ctx) { if (isDependencyTag()) { + if (tag.getMarkers().findFirst(Skip.class).isPresent()) { + return tag; + } ResolvedDependency d = findDependency(tag); if (d != null && matchesGroup(d) && @@ -409,4 +414,15 @@ private static class RemoveEmptyPluginsTags extends MavenIsoVisitor} should be retained, even when this recipe would otherwise remove it + * as redundant. + */ + @Value + @With + public static class Skip implements Marker { + UUID id; + } } diff --git a/rewrite-maven/src/test/java/org/openrewrite/maven/RemoveRedundantDependencyVersionsTest.java b/rewrite-maven/src/test/java/org/openrewrite/maven/RemoveRedundantDependencyVersionsTest.java index f74aca3f21..4b79692256 100644 --- a/rewrite-maven/src/test/java/org/openrewrite/maven/RemoveRedundantDependencyVersionsTest.java +++ b/rewrite-maven/src/test/java/org/openrewrite/maven/RemoveRedundantDependencyVersionsTest.java @@ -15,18 +15,22 @@ */ package org.openrewrite.maven; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import org.openrewrite.DocumentExample; import org.openrewrite.Issue; +import org.openrewrite.Tree; import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; import org.openrewrite.test.SourceSpec; +import org.openrewrite.xml.tree.Xml; import static java.util.Collections.singletonList; import static org.openrewrite.java.Assertions.mavenProject; import static org.openrewrite.maven.Assertions.pomXml; +import static org.openrewrite.test.RewriteTest.toRecipe; class RemoveRedundantDependencyVersionsTest implements RewriteTest { @@ -2282,4 +2286,192 @@ void exceptManagedDependencyShouldNotBeRemoved() { ) ); } + + @Nested + class SkipMarker { + + @Test + void dependencyWithSkipMarkerKeepsVersion() { + String pom = """ + + com.mycompany.app + my-app + 1 + + + + com.google.guava + guava + 29.0-jre + + + + + + com.google.guava + guava + 29.0-jre + + + + """; + rewriteRun( + spec -> spec.recipe(toRecipe(() -> new MavenIsoVisitor<>() { + @Override + public Xml.Tag visitTag(Xml.Tag tag, org.openrewrite.ExecutionContext ctx) { + Xml.Tag t = super.visitTag(tag, ctx); + if (isDependencyTag("com.google.guava", "guava") && + !t.getMarkers().findFirst(RemoveRedundantDependencyVersions.Skip.class).isPresent()) { + t = t.withMarkers(t.getMarkers().add(new RemoveRedundantDependencyVersions.Skip(Tree.randomId()))); + } + if (isProjectTag()) { + doAfterVisit(new RemoveRedundantDependencyVersions(null, null, null, null).getVisitor()); + } + return t; + } + })), + pomXml(pom, pom) + ); + } + + @Test + void dependencyWithoutSkipMarkerHasVersionRemoved() { + rewriteRun( + pomXml( + """ + + com.mycompany.app + my-app + 1 + + + + com.google.guava + guava + 29.0-jre + + + + + + com.google.guava + guava + 29.0-jre + + + + """, + """ + + com.mycompany.app + my-app + 1 + + + + com.google.guava + guava + 29.0-jre + + + + + + com.google.guava + guava + + + + """ + ) + ); + } + + @Test + void skipMarkerOnlyAffectsMarkedDependency() { + rewriteRun( + spec -> spec.recipe(toRecipe(() -> new MavenIsoVisitor<>() { + @Override + public Xml.Tag visitTag(Xml.Tag tag, org.openrewrite.ExecutionContext ctx) { + Xml.Tag t = super.visitTag(tag, ctx); + if (isDependencyTag("com.google.guava", "guava") && + !t.getMarkers().findFirst(RemoveRedundantDependencyVersions.Skip.class).isPresent()) { + t = t.withMarkers(t.getMarkers().add(new RemoveRedundantDependencyVersions.Skip(Tree.randomId()))); + } + if (isProjectTag()) { + doAfterVisit(new RemoveRedundantDependencyVersions(null, null, null, null).getVisitor()); + } + return t; + } + })), + pomXml( + """ + + com.mycompany.app + my-app + 1 + + + + com.google.guava + guava + 29.0-jre + + + commons-io + commons-io + 2.11.0 + + + + + + com.google.guava + guava + 29.0-jre + + + commons-io + commons-io + 2.11.0 + + + + """, + """ + + com.mycompany.app + my-app + 1 + + + + com.google.guava + guava + 29.0-jre + + + commons-io + commons-io + 2.11.0 + + + + + + com.google.guava + guava + 29.0-jre + + + commons-io + commons-io + + + + """ + ) + ); + } + } }