You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by gn...@apache.org on 2026/06/18 13:05:03 UTC
(maven) branch maven-4.0.x updated: Add deprecated property replacement (${version} → ${project.version}) to mvnup (#12308)
This is an automated email from the ASF dual-hosted git repository.
gnodet pushed a commit to branch maven-4.0.x
in repository https://gitbox.apache.org/repos/asf/maven.git
The following commit(s) were added to refs/heads/maven-4.0.x by this push:
new fe2a85addf Add deprecated property replacement (${version} → ${project.version}) to mvnup (#12308)
fe2a85addf is described below
commit fe2a85addfbbba7cbfab0196a3660a44b1bbdf01
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Thu Jun 18 15:04:57 2026 +0200
Add deprecated property replacement (${version} → ${project.version}) to mvnup (#12308)
Maven 3 supported ${version}, ${groupId}, and ${artifactId} as shorthand
aliases for their ${project.*} equivalents. Maven 4 drops these deprecated
aliases (see apache/maven#12304), causing builds to fail.
Add fixDeprecatedPropertyExpressions() to CompatibilityFixStrategy that
scans all text content in the POM and replaces the deprecated expressions
with their ${project.*} equivalents. This fix runs before the undefined
property expression check so that already-replaced expressions are not
falsely flagged.
Co-authored-by: Claude Sonnet 4.6 <no...@anthropic.com>
---
.../mvnup/goals/CompatibilityFixStrategy.java | 41 +++++
.../mvnup/goals/CompatibilityFixStrategyTest.java | 197 +++++++++++++++++++++
2 files changed, 238 insertions(+)
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 6b9b0c8e39..9b52f5d062 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
@@ -147,6 +147,7 @@ public UpgradeResult doApply(UpgradeContext context, Map<Path, Document> pomMap)
hasIssues |= fixDuplicateDependencies(pomDocument, context);
hasIssues |= fixDuplicatePlugins(pomDocument, context);
hasIssues |= fixUnsupportedRepositoryExpressions(pomDocument, context);
+ hasIssues |= fixDeprecatedPropertyExpressions(pomDocument, context);
hasIssues |= fixIncorrectParentRelativePaths(pomDocument, pomPath, pomMap, context);
hasIssues |= fixUndefinedPropertyExpressions(pomDocument, allDefinedProperties, context);
hasIssues |= fixUndefinedPropertyExpressionsInRepositories(pomDocument, allDefinedProperties, context);
@@ -759,6 +760,46 @@ private boolean fixRepositoryExpressions(
return fixed;
}
+ /**
+ * Fixes deprecated Maven 2/3 shorthand property expressions throughout the POM.
+ * Replaces ${version}, ${groupId}, and ${artifactId} with their ${project.*} equivalents,
+ * which are the only forms supported in Maven 4.
+ */
+ private boolean fixDeprecatedPropertyExpressions(Document pomDocument, UpgradeContext context) {
+ return fixDeprecatedPropertyExpressionsInElement(pomDocument.root(), context);
+ }
+
+ private boolean fixDeprecatedPropertyExpressionsInElement(Element element, UpgradeContext context) {
+ boolean fixed = false;
+
+ List<Element> children = element.childElements().toList();
+
+ if (children.isEmpty()) {
+ String text = element.textContent();
+ if (text != null && !text.isEmpty()) {
+ String fixedText = replaceDeprecatedExpressions(text);
+ if (!fixedText.equals(text)) {
+ element.textContent(fixedText);
+ context.detail("Fixed: replaced deprecated property expression in <" + element.name() + ">: "
+ + text.trim() + " → " + fixedText.trim());
+ fixed = true;
+ }
+ }
+ } else {
+ for (Element child : children) {
+ fixed |= fixDeprecatedPropertyExpressionsInElement(child, context);
+ }
+ }
+
+ return fixed;
+ }
+
+ private static String replaceDeprecatedExpressions(String text) {
+ return text.replace("${version}", "${project.version}")
+ .replace("${groupId}", "${project.groupId}")
+ .replace("${artifactId}", "${project.artifactId}");
+ }
+
private Path findParentPomInMap(
UpgradeContext context, String groupId, String artifactId, String version, Map<Path, Document> pomMap) {
return pomMap.entrySet().stream()
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 3b8e7fc9a0..2d1e6a6332 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
@@ -1632,6 +1632,203 @@ void shouldNotCommentOutRepositoryWithWellKnownProperty() throws Exception {
}
}
+ @Nested
+ @DisplayName("Deprecated Property Expression Fixes")
+ class DeprecatedPropertyExpressionFixesTests {
+
+ @Test
+ @DisplayName("should replace ${version} with ${project.version} in dependency version")
+ void shouldReplaceVersionExpression() 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>
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>test</groupId>
+ <artifactId>test-dep</artifactId>
+ <version>${version}</version>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <version>${version}</version>
+ </plugin>
+ </plugins>
+ </build>
+ </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 deprecated ${version}");
+
+ String xml = DomUtils.toXml(document);
+ assertFalse(xml.contains("${version}"), "Should not contain deprecated ${version}");
+ assertTrue(xml.contains("${project.version}"), "Should contain ${project.version}");
+ }
+
+ @Test
+ @DisplayName("should replace ${groupId} and ${artifactId} expressions")
+ void shouldReplaceGroupIdAndArtifactIdExpressions() 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>com.example</groupId>
+ <artifactId>my-project</artifactId>
+ <version>1.0.0</version>
+ <dependencies>
+ <dependency>
+ <groupId>${groupId}</groupId>
+ <artifactId>my-lib</artifactId>
+ <version>1.0.0</version>
+ </dependency>
+ <dependency>
+ <groupId>com.example</groupId>
+ <artifactId>${artifactId}-core</artifactId>
+ <version>1.0.0</version>
+ </dependency>
+ </dependencies>
+ </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 deprecated expressions");
+
+ String xml = DomUtils.toXml(document);
+ assertFalse(xml.contains("${groupId}"), "Should not contain deprecated ${groupId}");
+ assertFalse(xml.contains("${artifactId}"), "Should not contain deprecated ${artifactId}");
+ assertTrue(xml.contains("${project.groupId}"), "Should contain ${project.groupId}");
+ assertTrue(xml.contains("${project.artifactId}"), "Should contain ${project.artifactId}");
+ }
+
+ @Test
+ @DisplayName("should replace all three deprecated expressions in one POM")
+ void shouldReplaceAllThreeDeprecatedExpressions() 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>com.example</groupId>
+ <artifactId>my-project</artifactId>
+ <version>2.0.0</version>
+ <properties>
+ <selfCoords>${groupId}:${artifactId}:${version}</selfCoords>
+ </properties>
+ </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 deprecated expressions");
+
+ String xml = DomUtils.toXml(document);
+ assertFalse(xml.contains("${version}"), "Should not contain ${version}");
+ assertFalse(xml.contains("${groupId}"), "Should not contain ${groupId}");
+ assertFalse(xml.contains("${artifactId}"), "Should not contain ${artifactId}");
+ assertTrue(xml.contains("${project.version}"), "Should contain ${project.version}");
+ assertTrue(xml.contains("${project.groupId}"), "Should contain ${project.groupId}");
+ assertTrue(xml.contains("${project.artifactId}"), "Should contain ${project.artifactId}");
+ }
+
+ @Test
+ @DisplayName("should not modify POMs without deprecated expressions")
+ void shouldNotModifyPomsWithoutDeprecatedExpressions() 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>com.example</groupId>
+ <artifactId>my-project</artifactId>
+ <version>1.0.0</version>
+ <dependencies>
+ <dependency>
+ <groupId>com.example</groupId>
+ <artifactId>my-lib</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+ </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 POM without deprecated expressions");
+ }
+
+ @Test
+ @DisplayName("should replace deprecated expressions in plugin configuration")
+ void shouldReplaceDeprecatedExpressionsInPluginConfiguration() 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>com.example</groupId>
+ <artifactId>my-project</artifactId>
+ <version>1.0.0</version>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <configuration>
+ <archive>
+ <manifestEntries>
+ <Implementation-Version>${version}</Implementation-Version>
+ </manifestEntries>
+ </archive>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </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 deprecated ${version} in plugin config");
+
+ String xml = DomUtils.toXml(document);
+ assertFalse(xml.contains("${version}"), "Should not contain deprecated ${version}");
+ assertTrue(xml.contains("${project.version}"), "Should contain ${project.version}");
+ }
+ }
+
@Nested
@DisplayName("Strategy Description")
class StrategyDescriptionTests {