From 2932b831928caada21b613570153593e3926810e Mon Sep 17 00:00:00 2001 From: Jente Sondervorst Date: Tue, 31 Mar 2026 10:20:39 +0200 Subject: [PATCH] Fix Maven settings repos lost when context repos set independently When MavenSettings was non-null, getRepositories(MavenSettings, List) re-derived repos exclusively from settings profiles, ignoring any repos set directly on the context via setRepositories(). This caused download failures when repos were configured independently of settings profiles. Now both settings-derived repos and context repos are included, with settings repos taking precedence by ID. Fixes https://github.com/moderneinc/customer-requests/issues/2122 --- .../maven/MavenExecutionContextView.java | 21 ++- .../internal/MavenPomDownloaderTest.java | 166 ++++++++++++++++++ 2 files changed, 185 insertions(+), 2 deletions(-) diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/MavenExecutionContextView.java b/rewrite-maven/src/main/java/org/openrewrite/maven/MavenExecutionContextView.java index 73e29721cc6..49ae55012a7 100644 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/MavenExecutionContextView.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/MavenExecutionContextView.java @@ -188,10 +188,27 @@ public List getRepositories() { */ public List getRepositories(@Nullable MavenSettings mavenSettings, @Nullable List activeProfiles) { + List contextRepos = getMessage(MAVEN_REPOSITORIES, emptyList()); if (mavenSettings != null) { - return mapRepositories(mavenSettings, activeProfiles == null ? emptyList() : activeProfiles); + List settingsRepos = mapRepositories(mavenSettings, activeProfiles == null ? emptyList() : activeProfiles); + if (contextRepos.isEmpty()) { + return settingsRepos; + } + // Include both settings-derived repos and context repos, settings repos take precedence by ID + Map result = new LinkedHashMap<>(); + for (MavenRepository repo : settingsRepos) { + if (repo.getId() != null) { + result.put(repo.getId(), repo); + } + } + for (MavenRepository repo : contextRepos) { + if (repo.getId() != null) { + result.putIfAbsent(repo.getId(), repo); + } + } + return new ArrayList<>(result.values()); } - return getMessage(MAVEN_REPOSITORIES, emptyList()); + return contextRepos; } /** diff --git a/rewrite-maven/src/test/java/org/openrewrite/maven/internal/MavenPomDownloaderTest.java b/rewrite-maven/src/test/java/org/openrewrite/maven/internal/MavenPomDownloaderTest.java index 5456eddd97b..add7f3d6189 100755 --- a/rewrite-maven/src/test/java/org/openrewrite/maven/internal/MavenPomDownloaderTest.java +++ b/rewrite-maven/src/test/java/org/openrewrite/maven/internal/MavenPomDownloaderTest.java @@ -149,6 +149,172 @@ void repositoryOrder() { ); } + @Issue("https://github.com/moderneinc/customer-requests/issues/2122") + @Test + void settingsReposUsedWhenPomRepoFails(@TempDir Path tempDir) throws Exception { + // Create a working settings repo with the artifact + Path settingsRepoDir = tempDir.resolve("settings-repo"); + Path artifactDir = settingsRepoDir.resolve("com/example/my-lib/1.0.0"); + Files.createDirectories(artifactDir); + Files.writeString(artifactDir.resolve("my-lib-1.0.0.pom"), + //language=xml + """ + + com.example + my-lib + 1.0.0 + + """); + Files.writeString(artifactDir.resolve("my-lib-1.0.0.jar"), "I'm a jar"); + + // Create a broken POM repo (empty, has no artifacts) + Path brokenRepoDir = tempDir.resolve("broken-repo"); + Files.createDirectories(brokenRepoDir); + + // Set up Maven settings with the working repo in a profile + var ctx = MavenExecutionContextView.view(new InMemoryExecutionContext()); + ctx.setMavenSettings(MavenSettings.parse(Parser.Input.fromString(Path.of("settings.xml"), + //language=xml + """ + + + + my-repos + + + settings-repo + %s + + + + + + my-repos + + + """.formatted(settingsRepoDir.toUri()) + ), ctx)); + + var gav = new GroupArtifactVersion("com.example", "my-lib", "1.0.0"); + + // The POM repo doesn't have the artifact, but settings repo does. + // The download should succeed by falling through to the settings repo. + var brokenRepo = MavenRepository.builder() + .id("broken-repo") + .uri(brokenRepoDir.toUri().toString()) + .knownToExist(true) + .build(); + + Pom pom = assertDoesNotThrow(() -> + new MavenPomDownloader(emptyMap(), ctx) + .download(gav, null, null, List.of(brokenRepo))); + assertThat(pom.getGroupId()).isEqualTo("com.example"); + assertThat(pom.getArtifactId()).isEqualTo("my-lib"); + } + + @Issue("https://github.com/moderneinc/customer-requests/issues/2122") + @Test + void contextReposUsedWhenPomRepoFails(@TempDir Path tempDir) throws Exception { + // Create a working repo with the artifact + Path workingRepoDir = tempDir.resolve("working-repo"); + Path artifactDir = workingRepoDir.resolve("com/example/my-lib/1.0.0"); + Files.createDirectories(artifactDir); + Files.writeString(artifactDir.resolve("my-lib-1.0.0.pom"), + //language=xml + """ + + com.example + my-lib + 1.0.0 + + """); + Files.writeString(artifactDir.resolve("my-lib-1.0.0.jar"), "I'm a jar"); + + // Create a broken POM repo (empty, has no artifacts) + Path brokenRepoDir = tempDir.resolve("broken-repo"); + Files.createDirectories(brokenRepoDir); + + // Set repos directly on context (without MavenSettings) + var ctx = MavenExecutionContextView.view(new InMemoryExecutionContext()); + var workingRepo = MavenRepository.builder() + .id("working-repo") + .uri(workingRepoDir.toUri().toString()) + .knownToExist(true) + .build(); + ctx.setRepositories(List.of(workingRepo)); + + var gav = new GroupArtifactVersion("com.example", "my-lib", "1.0.0"); + + var brokenRepo = MavenRepository.builder() + .id("broken-repo") + .uri(brokenRepoDir.toUri().toString()) + .knownToExist(true) + .build(); + + // Download should succeed using context repo when POM repo fails + Pom pom = assertDoesNotThrow(() -> + new MavenPomDownloader(emptyMap(), ctx) + .download(gav, null, null, List.of(brokenRepo))); + assertThat(pom.getGroupId()).isEqualTo("com.example"); + assertThat(pom.getArtifactId()).isEqualTo("my-lib"); + } + + @Issue("https://github.com/moderneinc/customer-requests/issues/2122") + @Test + void contextReposNotLostWhenMavenSettingsPresent(@TempDir Path tempDir) throws Exception { + // Create a working repo with the artifact + Path workingRepoDir = tempDir.resolve("working-repo"); + Path artifactDir = workingRepoDir.resolve("com/example/my-lib/1.0.0"); + Files.createDirectories(artifactDir); + Files.writeString(artifactDir.resolve("my-lib-1.0.0.pom"), + //language=xml + """ + + com.example + my-lib + 1.0.0 + + """); + Files.writeString(artifactDir.resolve("my-lib-1.0.0.jar"), "I'm a jar"); + + // Create a broken POM repo (empty, has no artifacts) + Path brokenRepoDir = tempDir.resolve("broken-repo"); + Files.createDirectories(brokenRepoDir); + + // Set up Maven settings with NO profiles/repos + var ctx = MavenExecutionContextView.view(new InMemoryExecutionContext()); + ctx.setMavenSettings(MavenSettings.parse(Parser.Input.fromString(Path.of("settings.xml"), + //language=xml + """ + + + """ + ), ctx)); + + // Add repos directly on the context AFTER setting maven settings + var workingRepo = MavenRepository.builder() + .id("working-repo") + .uri(workingRepoDir.toUri().toString()) + .knownToExist(true) + .build(); + ctx.setRepositories(List.of(workingRepo)); + + var gav = new GroupArtifactVersion("com.example", "my-lib", "1.0.0"); + + var brokenRepo = MavenRepository.builder() + .id("broken-repo") + .uri(brokenRepoDir.toUri().toString()) + .knownToExist(true) + .build(); + + // Download should succeed using the context repo even though MavenSettings is set + Pom pom = assertDoesNotThrow(() -> + new MavenPomDownloader(emptyMap(), ctx) + .download(gav, null, null, List.of(brokenRepo))); + assertThat(pom.getGroupId()).isEqualTo("com.example"); + assertThat(pom.getArtifactId()).isEqualTo("my-lib"); + } + @Nested class WithNativeHttpURLConnectionAndTLS { private final ExecutionContext ctx = HttpSenderExecutionContextView.view(new InMemoryExecutionContext())