diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/PluginUpgradeStrategy.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/PluginUpgradeStrategy.java index e9d145bdc551..40884137006b 100644 --- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/PluginUpgradeStrategy.java +++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/PluginUpgradeStrategy.java @@ -64,6 +64,8 @@ import static eu.maveniverse.domtrip.maven.MavenPomElements.Elements.ARTIFACT_ID; import static eu.maveniverse.domtrip.maven.MavenPomElements.Elements.BUILD; +import static eu.maveniverse.domtrip.maven.MavenPomElements.Elements.DEPENDENCIES; +import static eu.maveniverse.domtrip.maven.MavenPomElements.Elements.DEPENDENCY; import static eu.maveniverse.domtrip.maven.MavenPomElements.Elements.GROUP_ID; import static eu.maveniverse.domtrip.maven.MavenPomElements.Elements.PARENT; import static eu.maveniverse.domtrip.maven.MavenPomElements.Elements.PLUGIN; @@ -100,6 +102,12 @@ public class PluginUpgradeStrategy extends AbstractUpgradeStrategy { "3.0.0", MAVEN_4_COMPATIBILITY_REASON)); + private static final List PLUGIN_DEPENDENCY_UPGRADES = List.of(new PluginUpgrade( + "org.codehaus.mojo", + "extra-enforcer-rules", + "1.4", + "Versions before 1.4 use a removed DependencyGraphBuilder API incompatible with Maven 4")); + private Session session; @Inject @@ -262,6 +270,7 @@ private boolean upgradePluginsInSection( return pluginsElement .children(PLUGIN) .map(pluginElement -> { + boolean upgraded = false; String groupId = getChildText(pluginElement, GROUP_ID); String artifactId = getChildText(pluginElement, ARTIFACT_ID); @@ -275,10 +284,13 @@ private boolean upgradePluginsInSection( PluginUpgradeInfo upgrade = pluginUpgrades.get(pluginKey); if (upgrade != null) { - return upgradePluginVersion(pluginElement, upgrade, pomDocument, sectionName, context); + upgraded = upgradePluginVersion(pluginElement, upgrade, pomDocument, sectionName, context); } } - return false; + + upgraded |= upgradePluginDependencies(pluginElement, pomDocument, sectionName, context); + + return upgraded; }) .reduce(false, Boolean::logicalOr); } @@ -370,6 +382,46 @@ private boolean upgradePropertyVersion( return false; } + /** + * Upgrades plugin dependencies (e.g., extra-enforcer-rules inside maven-enforcer-plugin). + */ + private boolean upgradePluginDependencies( + Element pluginElement, Document pomDocument, String sectionName, UpgradeContext context) { + Element dependenciesElement = pluginElement.child(DEPENDENCIES).orElse(null); + if (dependenciesElement == null) { + return false; + } + + Map depUpgrades = getPluginDependencyUpgradesMap(); + + return dependenciesElement + .children(DEPENDENCY) + .map(depElement -> { + String groupId = getChildText(depElement, GROUP_ID); + String artifactId = getChildText(depElement, ARTIFACT_ID); + + if (groupId != null && artifactId != null) { + String depKey = groupId + ":" + artifactId; + PluginUpgradeInfo upgrade = depUpgrades.get(depKey); + + if (upgrade != null) { + return upgradePluginVersion( + depElement, upgrade, pomDocument, sectionName + "/plugin/dependencies", context); + } + } + return false; + }) + .reduce(false, Boolean::logicalOr); + } + + private Map getPluginDependencyUpgradesMap() { + return PLUGIN_DEPENDENCY_UPGRADES.stream() + .collect(Collectors.toMap( + upgrade -> upgrade.groupId() + ":" + upgrade.artifactId(), + upgrade -> + new PluginUpgradeInfo(upgrade.groupId(), upgrade.artifactId(), upgrade.minVersion()))); + } + /** * Simple version comparison to check if current version is below minimum version. * This is a basic implementation that works for most Maven plugin versions. diff --git a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvnup/goals/PluginUpgradeStrategyTest.java b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvnup/goals/PluginUpgradeStrategyTest.java index 49abdf2be51a..bcd803924a0a 100644 --- a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvnup/goals/PluginUpgradeStrategyTest.java +++ b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvnup/goals/PluginUpgradeStrategyTest.java @@ -414,6 +414,93 @@ void shouldNotUpgradeWhenPropertyNotFound() throws Exception { } } + @Nested + @DisplayName("Plugin Dependency Upgrades") + class PluginDependencyUpgradeTests { + + @Test + @DisplayName("should upgrade extra-enforcer-rules dependency when below minimum") + void shouldUpgradeExtraEnforcerRulesDependency() throws Exception { + String pomXml = """ + + + 4.0.0 + test + test + 1.0.0 + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.5.0 + + + org.codehaus.mojo + extra-enforcer-rules + 1.0-beta-4 + + + + + + + """; + + Document document = Document.of(pomXml); + Map pomMap = Map.of(Paths.get("pom.xml"), document); + + UpgradeContext context = createMockContext(); + UpgradeResult result = strategy.doApply(context, pomMap); + + assertTrue(result.success(), "Plugin dependency upgrade should succeed"); + assertTrue(result.modifiedCount() > 0, "Should have upgraded extra-enforcer-rules"); + + String xml = document.toXml(); + assertTrue(xml.contains("1.4"), "extra-enforcer-rules should be upgraded to 1.4"); + assertFalse(xml.contains("1.0-beta-4"), "Old version should be gone"); + } + + @Test + @DisplayName("should not upgrade extra-enforcer-rules when version is already sufficient") + void shouldNotUpgradeExtraEnforcerRulesWhenSufficient() throws Exception { + String pomXml = """ + + + 4.0.0 + test + test + 1.0.0 + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.5.0 + + + org.codehaus.mojo + extra-enforcer-rules + 1.8.0 + + + + + + + """; + + Document document = Document.of(pomXml); + Map pomMap = Map.of(Paths.get("pom.xml"), document); + + UpgradeContext context = createMockContext(); + strategy.doApply(context, pomMap); + + String xml = document.toXml(); + assertTrue(xml.contains("1.8.0"), "Version 1.8.0 should be preserved"); + } + } + @Nested @DisplayName("Plugin Management") class PluginManagementTests {