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 <[email protected]>
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 <[email protected]>
---
.../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 {