diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/plugins/AddSettingsPluginRepository.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/plugins/AddSettingsPluginRepository.java index 25b3490d476..1d602b8aa5e 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/plugins/AddSettingsPluginRepository.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/plugins/AddSettingsPluginRepository.java @@ -138,6 +138,12 @@ private List addPluginManagementRepos(List statements, J p if (mapped != statements) { return mapped; } + // Check if pluginManagement exists but no change was needed (repo already present) + for (Statement s : statements) { + if (unwrapMethodCall(s, "pluginManagement") != null) { + return statements; + } + } // No existing pluginManagement found — insert after any leading imports Statement pluginManagementStatement = pluginManagement instanceof J.Block ? ((J.Block) pluginManagement).getStatements().get(0) : @@ -190,6 +196,11 @@ private Statement addRepoToRepositoriesBlock(Statement statement, J pluginManage return statement; } J.MethodInvocation repoToAdd = extractRepository(pluginManagement); + + if (repoAlreadyExists(repos, repoToAdd.getSimpleName())) { + return statement; + } + J.MethodInvocation m2 = repos.withArguments(ListUtils.mapFirst(repos.getArguments(), arg2 -> { if (!(arg2 instanceof J.Lambda) || !(((J.Lambda) arg2).getBody() instanceof J.Block)) { return arg2; @@ -208,6 +219,23 @@ private Statement addRepoToRepositoriesBlock(Statement statement, J pluginManage return rewrap(statement, m2); } + // Name-based fallback for when MethodMatcher fails due to incorrect type attribution (e.g. rewrite-kotlin) + private boolean repoAlreadyExists(J.MethodInvocation repos, String repoName) { + if (repos.getArguments().isEmpty() || !(repos.getArguments().get(0) instanceof J.Lambda)) { + return false; + } + J.Lambda lambda = (J.Lambda) repos.getArguments().get(0); + if (!(lambda.getBody() instanceof J.Block)) { + return false; + } + for (Statement s : ((J.Block) lambda.getBody()).getStatements()) { + if (unwrapMethodCall(s, repoName) != null) { + return true; + } + } + return false; + } + private J generatePluginManagementBlock(Class compilationUnitClass, Function methodExtractor, ExecutionContext ctx) { String code; if (url == null) { diff --git a/rewrite-gradle/src/test/java/org/openrewrite/gradle/plugins/AddSettingsPluginRepositoryTest.java b/rewrite-gradle/src/test/java/org/openrewrite/gradle/plugins/AddSettingsPluginRepositoryTest.java index 3ea6f65d872..eeb957fe31d 100644 --- a/rewrite-gradle/src/test/java/org/openrewrite/gradle/plugins/AddSettingsPluginRepositoryTest.java +++ b/rewrite-gradle/src/test/java/org/openrewrite/gradle/plugins/AddSettingsPluginRepositoryTest.java @@ -17,6 +17,10 @@ import org.junit.jupiter.api.Test; import org.openrewrite.DocumentExample; +import org.openrewrite.SourceFile; +import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.JavaType; import org.openrewrite.test.RewriteTest; import static org.openrewrite.gradle.Assertions.settingsGradle; @@ -584,6 +588,60 @@ void noPluginManagementBlockWithBuildCacheKts() { ); } + /** + * Simulate platform behavior where KTS-parsed settings.gradle.kts has incomplete type + * attribution — method types have a wrong declaring type instead of being null. + */ + @SuppressWarnings("unchecked") + private static T corruptMethodTypes(T sourceFile) { + JavaType.FullyQualified wrongType = JavaType.ShallowClass.build("kotlin.Unit"); + return (T) new JavaIsoVisitor() { + @Override + public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, Integer p) { + J.MethodInvocation m = super.visitMethodInvocation(method, p); + if (m.getMethodType() != null) { + return m.withMethodType(m.getMethodType().withDeclaringType(wrongType)); + } + return m; + } + }.visitNonNull(sourceFile, 0); + } + + @Test + void skipWhenExistsGradlePluginPortalKtsWithoutTypeAttribution() { + rewriteRun( + spec -> spec.recipe(new AddSettingsPluginRepository("gradlePluginPortal", null)), + settingsGradleKts( + """ + pluginManagement { + repositories { + gradlePluginPortal() + } + } + """, + spec -> spec.mapBeforeRecipe(AddSettingsPluginRepositoryTest::corruptMethodTypes) + ) + ); + } + + @Test + void skipWhenExistsGradlePluginPortalWithOtherReposKtsWithoutTypeAttribution() { + rewriteRun( + spec -> spec.recipe(new AddSettingsPluginRepository("gradlePluginPortal", null)), + settingsGradleKts( + """ + pluginManagement { + repositories { + mavenLocal() + gradlePluginPortal() + } + } + """, + spec -> spec.mapBeforeRecipe(AddSettingsPluginRepositoryTest::corruptMethodTypes) + ) + ); + } + @Test void addToExistingPluginManagementWithPluginsBlockKts() { rewriteRun(