This is an automated email from the ASF dual-hosted git repository. gnodet pushed a commit to branch backport/maven-4.0.x/pr-12105 in repository https://gitbox.apache.org/repos/asf/maven.git
commit dc30cbfef5074757ef2ab3d69c517aa2b9e3adec Author: Guillaume Nodet <[email protected]> AuthorDate: Tue May 19 14:51:21 2026 +0200 [maven-4.0.x] Fix mvnup to replace deprecated ${basedir} in repository URLs (#12105) Co-Authored-By: Claude Opus 4.6 <[email protected]> --- .../mvnup/goals/CompatibilityFixStrategy.java | 12 +- .../mvnup/goals/CompatibilityFixStrategyTest.java | 197 +++++++++++++++++++++ 2 files changed, 204 insertions(+), 5 deletions(-) diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/CompatibilityFixStrategy.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/CompatibilityFixStrategy.java index 69e09e0a0d..ff572d49b4 100644 --- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/CompatibilityFixStrategy.java +++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/CompatibilityFixStrategy.java @@ -497,12 +497,14 @@ private boolean fixRepositoryExpressions(Element repositoriesElement, Namespace Element urlElement = repository.getChild("url", namespace); if (urlElement != null) { String url = urlElement.getTextTrim(); - if (url.contains("${")) { - // Allow repository URL interpolation; do not disable. - // Keep a gentle warning to help users notice unresolved placeholders at build time. + String fixedUrl = + url.replace("${basedir}", "${project.basedir}").replace("${pom.basedir}", "${project.basedir}"); + if (!fixedUrl.equals(url)) { + urlElement.setText(fixedUrl); String repositoryId = getChildText(repository, "id", namespace); - context.info("Detected interpolated expression in " + elementType + " URL (id: " + repositoryId - + "): " + url); + context.detail("Fixed: replaced deprecated expression in " + elementType + " URL (id: " + + repositoryId + "): " + url + " → " + fixedUrl); + fixed = true; } } } diff --git a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvnup/goals/CompatibilityFixStrategyTest.java b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvnup/goals/CompatibilityFixStrategyTest.java index 0a26906012..12bbf60f89 100644 --- a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvnup/goals/CompatibilityFixStrategyTest.java +++ b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvnup/goals/CompatibilityFixStrategyTest.java @@ -287,6 +287,203 @@ void shouldRemoveDuplicatePluginsInPluginManagement() throws Exception { } } + @Nested + @DisplayName("Repository Expression Fixes") + class RepositoryExpressionFixesTests { + + @Test + @DisplayName("should replace ${basedir} with ${project.basedir} in repository URLs") + void shouldReplaceBasedirInRepositoryUrls() throws Exception { + String pomXml = """ + <?xml version="1.0" encoding="UTF-8"?> + <project xmlns="http://maven.apache.org/POM/4.0.0"> + <modelVersion>4.0.0</modelVersion> + <groupId>test</groupId> + <artifactId>test</artifactId> + <version>1.0.0</version> + <repositories> + <repository> + <id>local-repo</id> + <url>file://${basedir}/internal-repository</url> + </repository> + </repositories> + </project> + """; + + Document document = saxBuilder.build(new StringReader(pomXml)); + Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document); + + UpgradeContext context = createMockContext(); + UpgradeResult result = strategy.apply(context, pomMap); + + assertTrue(result.success(), "Compatibility fix should succeed"); + assertTrue(result.modifiedCount() > 0, "Should have fixed basedir expression"); + + Element root = document.getRootElement(); + Element repositories = root.getChild("repositories", root.getNamespace()); + Element repository = repositories.getChild("repository", root.getNamespace()); + Element url = repository.getChild("url", root.getNamespace()); + assertEquals( + "file://${project.basedir}/internal-repository", + url.getTextTrim(), + "Should have replaced ${basedir} with ${project.basedir}"); + } + + @Test + @DisplayName("should replace ${pom.basedir} with ${project.basedir} in repository URLs") + void shouldReplacePomBasedirInRepositoryUrls() throws Exception { + String pomXml = """ + <?xml version="1.0" encoding="UTF-8"?> + <project xmlns="http://maven.apache.org/POM/4.0.0"> + <modelVersion>4.0.0</modelVersion> + <groupId>test</groupId> + <artifactId>test</artifactId> + <version>1.0.0</version> + <repositories> + <repository> + <id>local-repo</id> + <url>file://${pom.basedir}/lib</url> + </repository> + </repositories> + </project> + """; + + Document document = saxBuilder.build(new StringReader(pomXml)); + Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document); + + UpgradeContext context = createMockContext(); + UpgradeResult result = strategy.apply(context, pomMap); + + assertTrue(result.success(), "Compatibility fix should succeed"); + assertTrue(result.modifiedCount() > 0, "Should have fixed pom.basedir expression"); + + Element root = document.getRootElement(); + Element repositories = root.getChild("repositories", root.getNamespace()); + Element repository = repositories.getChild("repository", root.getNamespace()); + Element url = repository.getChild("url", root.getNamespace()); + assertEquals( + "file://${project.basedir}/lib", + url.getTextTrim(), + "Should have replaced ${pom.basedir} with ${project.basedir}"); + } + + @Test + @DisplayName("should replace ${basedir} in pluginRepository URLs") + void shouldReplaceBasedirInPluginRepositoryUrls() throws Exception { + String pomXml = """ + <?xml version="1.0" encoding="UTF-8"?> + <project xmlns="http://maven.apache.org/POM/4.0.0"> + <modelVersion>4.0.0</modelVersion> + <groupId>test</groupId> + <artifactId>test</artifactId> + <version>1.0.0</version> + <pluginRepositories> + <pluginRepository> + <id>local-plugins</id> + <url>file://${basedir}/plugin-repo</url> + </pluginRepository> + </pluginRepositories> + </project> + """; + + Document document = saxBuilder.build(new StringReader(pomXml)); + Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document); + + UpgradeContext context = createMockContext(); + UpgradeResult result = strategy.apply(context, pomMap); + + assertTrue(result.success(), "Compatibility fix should succeed"); + assertTrue(result.modifiedCount() > 0, "Should have fixed basedir in pluginRepository"); + + Element root = document.getRootElement(); + Element pluginRepositories = root.getChild("pluginRepositories", root.getNamespace()); + Element pluginRepository = pluginRepositories.getChild("pluginRepository", root.getNamespace()); + Element url = pluginRepository.getChild("url", root.getNamespace()); + assertEquals( + "file://${project.basedir}/plugin-repo", + url.getTextTrim(), + "Should have replaced ${basedir} with ${project.basedir}"); + } + + @Test + @DisplayName("should replace ${basedir} in profile repository URLs") + void shouldReplaceBasedirInProfileRepositoryUrls() throws Exception { + String pomXml = """ + <?xml version="1.0" encoding="UTF-8"?> + <project xmlns="http://maven.apache.org/POM/4.0.0"> + <modelVersion>4.0.0</modelVersion> + <groupId>test</groupId> + <artifactId>test</artifactId> + <version>1.0.0</version> + <profiles> + <profile> + <id>local</id> + <repositories> + <repository> + <id>local-repo</id> + <url>file://${basedir}/repo</url> + </repository> + </repositories> + </profile> + </profiles> + </project> + """; + + Document document = saxBuilder.build(new StringReader(pomXml)); + Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document); + + UpgradeContext context = createMockContext(); + UpgradeResult result = strategy.apply(context, pomMap); + + assertTrue(result.success(), "Compatibility fix should succeed"); + assertTrue(result.modifiedCount() > 0, "Should have fixed basedir in profile repository"); + + Element root = document.getRootElement(); + Element profiles = root.getChild("profiles", root.getNamespace()); + Element profile = profiles.getChild("profile", root.getNamespace()); + Element repositories = profile.getChild("repositories", root.getNamespace()); + Element repository = repositories.getChild("repository", root.getNamespace()); + Element url = repository.getChild("url", root.getNamespace()); + assertEquals( + "file://${project.basedir}/repo", + url.getTextTrim(), + "Should have replaced ${basedir} with ${project.basedir}"); + } + + @Test + @DisplayName("should not modify repository URLs without deprecated expressions") + void shouldNotModifyUrlsWithoutDeprecatedExpressions() throws Exception { + String pomXml = """ + <?xml version="1.0" encoding="UTF-8"?> + <project xmlns="http://maven.apache.org/POM/4.0.0"> + <modelVersion>4.0.0</modelVersion> + <groupId>test</groupId> + <artifactId>test</artifactId> + <version>1.0.0</version> + <repositories> + <repository> + <id>central</id> + <url>https://repo.maven.apache.org/maven2</url> + </repository> + <repository> + <id>local-repo</id> + <url>file://${project.basedir}/repo</url> + </repository> + </repositories> + </project> + """; + + Document document = saxBuilder.build(new StringReader(pomXml)); + Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document); + + UpgradeContext context = createMockContext(); + UpgradeResult result = strategy.apply(context, pomMap); + + assertTrue(result.success(), "Compatibility fix should succeed"); + assertEquals(0, result.modifiedCount(), "Should not have modified any POMs"); + } + } + @Nested @DisplayName("Strategy Description") class StrategyDescriptionTests {
