This is an automated email from the ASF dual-hosted git repository. gnodet pushed a commit to branch opalescent-rumba in repository https://gitbox.apache.org/repos/asf/maven.git
commit e0ec29698707f1ebf6aa82c1cd85df5ac7848528 Author: Guillaume Nodet <[email protected]> AuthorDate: Tue May 19 14:51:21 2026 +0200 Fix mvnup to replace deprecated ${basedir} in repository URLs Maven 4 validates that repository URLs are fully interpolated (MNG-8677, PR #2158). When a POM uses ${basedir} or ${pom.basedir} in repository URLs, this can fail validation since these are deprecated forms that may not be interpolated in all contexts. The compatibility fix strategy now replaces ${basedir} and ${pom.basedir} with the canonical ${project.basedir} form in repository and pluginRepository URL elements, including those inside profiles. See: https://github.com/gnodet/maven4-testing/issues/13299 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 af9064c11c..7a141c8f5a 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 @@ -466,12 +466,14 @@ private boolean fixRepositoryExpressions( Element urlElement = repository.child("url").orElse(null); if (urlElement != null) { String url = urlElement.textContent().trim(); - 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.textContent(fixedUrl); String repositoryId = repository.childText("id"); - 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 af8b0774c7..bfe296769b 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 @@ -281,6 +281,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 = Document.of(pomXml); + Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document); + + UpgradeContext context = createMockContext(); + UpgradeResult result = strategy.doApply(context, pomMap); + + assertTrue(result.success(), "Compatibility fix should succeed"); + assertTrue(result.modifiedCount() > 0, "Should have fixed basedir expression"); + + Element root = document.root(); + Element repositories = DomUtils.findChildElement(root, "repositories"); + Element repository = DomUtils.findChildElement(repositories, "repository"); + Element url = DomUtils.findChildElement(repository, "url"); + assertEquals( + "file://${project.basedir}/internal-repository", + url.textContent().trim(), + "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 = Document.of(pomXml); + Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document); + + UpgradeContext context = createMockContext(); + UpgradeResult result = strategy.doApply(context, pomMap); + + assertTrue(result.success(), "Compatibility fix should succeed"); + assertTrue(result.modifiedCount() > 0, "Should have fixed pom.basedir expression"); + + Element root = document.root(); + Element repositories = DomUtils.findChildElement(root, "repositories"); + Element repository = DomUtils.findChildElement(repositories, "repository"); + Element url = DomUtils.findChildElement(repository, "url"); + assertEquals( + "file://${project.basedir}/lib", + url.textContent().trim(), + "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 = Document.of(pomXml); + Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document); + + UpgradeContext context = createMockContext(); + UpgradeResult result = strategy.doApply(context, pomMap); + + assertTrue(result.success(), "Compatibility fix should succeed"); + assertTrue(result.modifiedCount() > 0, "Should have fixed basedir in pluginRepository"); + + Element root = document.root(); + Element pluginRepositories = DomUtils.findChildElement(root, "pluginRepositories"); + Element pluginRepository = DomUtils.findChildElement(pluginRepositories, "pluginRepository"); + Element url = DomUtils.findChildElement(pluginRepository, "url"); + assertEquals( + "file://${project.basedir}/plugin-repo", + url.textContent().trim(), + "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 = Document.of(pomXml); + Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document); + + UpgradeContext context = createMockContext(); + UpgradeResult result = strategy.doApply(context, pomMap); + + assertTrue(result.success(), "Compatibility fix should succeed"); + assertTrue(result.modifiedCount() > 0, "Should have fixed basedir in profile repository"); + + Element root = document.root(); + Element profiles = DomUtils.findChildElement(root, "profiles"); + Element profile = DomUtils.findChildElement(profiles, "profile"); + Element repositories = DomUtils.findChildElement(profile, "repositories"); + Element repository = DomUtils.findChildElement(repositories, "repository"); + Element url = DomUtils.findChildElement(repository, "url"); + assertEquals( + "file://${project.basedir}/repo", + url.textContent().trim(), + "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 = Document.of(pomXml); + Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document); + + UpgradeContext context = createMockContext(); + UpgradeResult result = strategy.doApply(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 {
