Skip to content

Commit 18e5bb7

Browse files
pstreefJenson3210
andauthored
Fix duplicate gradlePluginPortal() in settings.gradle.kts (#7230)
* Add failing tests for duplicate gradlePluginPortal() in KTS with wrong type attribution When method types have an incorrect declaring type (as happens on the platform with KTS-parsed settings.gradle.kts), FindRepository's MethodMatcher fails to detect existing repositories, causing AddSettingsPluginRepository to add duplicates. Reproduces moderneinc/customer-requests#2120 * Fix duplicate gradlePluginPortal() in settings.gradle.kts with incorrect type attribution On the Moderne platform, KTS-parsed settings.gradle.kts files may have incorrect type attribution on method invocations (e.g. wrong declaring type instead of RepositoryHandler). This causes FindRepository's MethodMatcher to fail the strict matching path, missing existing repository entries and leading AddSettingsPluginRepository to add duplicates. Added two defensive checks: 1. In addRepoToRepositoriesBlock: name-based check if the repository already exists in the repositories block before adding 2. In addPluginManagementRepos: detect when pluginManagement exists but no change was needed, to avoid inserting a duplicate block Fixes moderneinc/customer-requests#2120 * Clarify comment: workaround for Kotlin parser type attribution issue * Consolidate comment on repoAlreadyExists method --------- Co-authored-by: Jente Sondervorst <jentesondervorst@gmail.com>
1 parent 5b212d9 commit 18e5bb7

File tree

2 files changed

+86
-0
lines changed

2 files changed

+86
-0
lines changed

rewrite-gradle/src/main/java/org/openrewrite/gradle/plugins/AddSettingsPluginRepository.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,12 @@ private List<Statement> addPluginManagementRepos(List<Statement> statements, J p
138138
if (mapped != statements) {
139139
return mapped;
140140
}
141+
// Check if pluginManagement exists but no change was needed (repo already present)
142+
for (Statement s : statements) {
143+
if (unwrapMethodCall(s, "pluginManagement") != null) {
144+
return statements;
145+
}
146+
}
141147
// No existing pluginManagement found — insert after any leading imports
142148
Statement pluginManagementStatement = pluginManagement instanceof J.Block ?
143149
((J.Block) pluginManagement).getStatements().get(0) :
@@ -190,6 +196,11 @@ private Statement addRepoToRepositoriesBlock(Statement statement, J pluginManage
190196
return statement;
191197
}
192198
J.MethodInvocation repoToAdd = extractRepository(pluginManagement);
199+
200+
if (repoAlreadyExists(repos, repoToAdd.getSimpleName())) {
201+
return statement;
202+
}
203+
193204
J.MethodInvocation m2 = repos.withArguments(ListUtils.mapFirst(repos.getArguments(), arg2 -> {
194205
if (!(arg2 instanceof J.Lambda) || !(((J.Lambda) arg2).getBody() instanceof J.Block)) {
195206
return arg2;
@@ -208,6 +219,23 @@ private Statement addRepoToRepositoriesBlock(Statement statement, J pluginManage
208219
return rewrap(statement, m2);
209220
}
210221

222+
// Name-based fallback for when MethodMatcher fails due to incorrect type attribution (e.g. rewrite-kotlin)
223+
private boolean repoAlreadyExists(J.MethodInvocation repos, String repoName) {
224+
if (repos.getArguments().isEmpty() || !(repos.getArguments().get(0) instanceof J.Lambda)) {
225+
return false;
226+
}
227+
J.Lambda lambda = (J.Lambda) repos.getArguments().get(0);
228+
if (!(lambda.getBody() instanceof J.Block)) {
229+
return false;
230+
}
231+
for (Statement s : ((J.Block) lambda.getBody()).getStatements()) {
232+
if (unwrapMethodCall(s, repoName) != null) {
233+
return true;
234+
}
235+
}
236+
return false;
237+
}
238+
211239
private <T extends JavaSourceFile> J generatePluginManagementBlock(Class<T> compilationUnitClass, Function<T, J> methodExtractor, ExecutionContext ctx) {
212240
String code;
213241
if (url == null) {

rewrite-gradle/src/test/java/org/openrewrite/gradle/plugins/AddSettingsPluginRepositoryTest.java

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717

1818
import org.junit.jupiter.api.Test;
1919
import org.openrewrite.DocumentExample;
20+
import org.openrewrite.SourceFile;
21+
import org.openrewrite.java.JavaIsoVisitor;
22+
import org.openrewrite.java.tree.J;
23+
import org.openrewrite.java.tree.JavaType;
2024
import org.openrewrite.test.RewriteTest;
2125

2226
import static org.openrewrite.gradle.Assertions.settingsGradle;
@@ -584,6 +588,60 @@ void noPluginManagementBlockWithBuildCacheKts() {
584588
);
585589
}
586590

591+
/**
592+
* Simulate platform behavior where KTS-parsed settings.gradle.kts has incomplete type
593+
* attribution — method types have a wrong declaring type instead of being null.
594+
*/
595+
@SuppressWarnings("unchecked")
596+
private static <T extends SourceFile> T corruptMethodTypes(T sourceFile) {
597+
JavaType.FullyQualified wrongType = JavaType.ShallowClass.build("kotlin.Unit");
598+
return (T) new JavaIsoVisitor<Integer>() {
599+
@Override
600+
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, Integer p) {
601+
J.MethodInvocation m = super.visitMethodInvocation(method, p);
602+
if (m.getMethodType() != null) {
603+
return m.withMethodType(m.getMethodType().withDeclaringType(wrongType));
604+
}
605+
return m;
606+
}
607+
}.visitNonNull(sourceFile, 0);
608+
}
609+
610+
@Test
611+
void skipWhenExistsGradlePluginPortalKtsWithoutTypeAttribution() {
612+
rewriteRun(
613+
spec -> spec.recipe(new AddSettingsPluginRepository("gradlePluginPortal", null)),
614+
settingsGradleKts(
615+
"""
616+
pluginManagement {
617+
repositories {
618+
gradlePluginPortal()
619+
}
620+
}
621+
""",
622+
spec -> spec.mapBeforeRecipe(AddSettingsPluginRepositoryTest::corruptMethodTypes)
623+
)
624+
);
625+
}
626+
627+
@Test
628+
void skipWhenExistsGradlePluginPortalWithOtherReposKtsWithoutTypeAttribution() {
629+
rewriteRun(
630+
spec -> spec.recipe(new AddSettingsPluginRepository("gradlePluginPortal", null)),
631+
settingsGradleKts(
632+
"""
633+
pluginManagement {
634+
repositories {
635+
mavenLocal()
636+
gradlePluginPortal()
637+
}
638+
}
639+
""",
640+
spec -> spec.mapBeforeRecipe(AddSettingsPluginRepositoryTest::corruptMethodTypes)
641+
)
642+
);
643+
}
644+
587645
@Test
588646
void addToExistingPluginManagementWithPluginsBlockKts() {
589647
rewriteRun(

0 commit comments

Comments
 (0)