Skip to content

mvnup: Quarkus plugin upgrade, CLI robustness, and Maven 4 compatibility warnings#12430

Open
gnodet wants to merge 9 commits into
apache:masterfrom
gnodet:fix-https-github-com-apache-maven-issues-12429
Open

mvnup: Quarkus plugin upgrade, CLI robustness, and Maven 4 compatibility warnings#12430
gnodet wants to merge 9 commits into
apache:masterfrom
gnodet:fix-https-github-com-apache-maven-issues-12429

Conversation

@gnodet

@gnodet gnodet commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR adds several mvnup improvements:

Details

Quarkus plugin upgrade (#12429)

Quarkus plugin versions < 3.26.0 use smallrye-beanbag to bootstrap the Maven Resolver, which is incompatible with Maven 4's reorganized Aether API (see quarkusio/quarkus#37627, fixed by PR #48248, milestone 3.26.0.CR1). Many Quarkus projects reuse a single property (e.g. ${quarkus.platform.version}) for both the platform BOMs and the plugin version. When mvnup bumps the plugin version, it introduces a separate quarkus-plugin.version property to avoid altering the BOM-managed dependency tree.

CLI robustness (#12431)

mvnup inherits the Maven launcher's argument handling, which appends options from .mvn/maven.config. The fix overrides CLIManager.parse() to catch UnrecognizedOptionException and retry after removing the unrecognized option.

gmavenplus-plugin upgrade (#12432)

gmavenplus-plugin versions < 4.2.0 call mutating methods on immutable lists returned by the Maven 4 API (see MNG-8657). This was fixed in gmavenplus 4.2.0 via groovy/GMavenPlus#328. mvnup now auto-upgrades to 4.2.0 as a minimum version.

Maven 4 compatibility warnings (#12434, #12435)

Two warning-only checks in CompatibilityFixStrategy that detect patterns known to fail with Maven 4 but which cannot be auto-fixed. These emit warnings via context.warning() without modifying POMs or affecting the hasIssues flag.

Note: an initial implementation of a prefix-filtering warning (#12433) was included and then reverted — Maven 4's prefix filter defaults to permissive behavior (noInputOutcome=true) when no prefixes.txt exists, so the blanket warning was incorrect.

Test plan

  • Quarkus plugin upgrade: 10 tests covering basic upgrade, BOM decoupling, version gap warning, pluginManagement
  • CLI option parsing: 6 tests for -ntp, -U, -T4, --no-transfer-progress with mvnup options
  • Incompatible plugin warning: 2 tests (no false positive for compatible plugins, gmavenplus now handled as upgrade)
  • Property-interpolated module paths: 3 tests (root modules, static paths no warn, profile modules)
  • CI-friendly dependency versions: 5 tests (${revision}, non-CI-friendly no warn, explicit versions, ${changelist}, no parent)
  • All existing tests pass

🤖 Generated with Claude Code

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends the mvnup plugin-upgrade logic to specifically handle Quarkus’ quarkus-maven-plugin (both io.quarkus and io.quarkus.platform) to ensure Maven 4 compatibility, including safe handling of shared BOM/version properties and user-facing warnings about platform/plugin version gaps.

Changes:

  • Add predefined PluginUpgrade entries for quarkus-maven-plugin with a minimum version of 3.26.0 and an explicit Maven 4 compatibility rationale.
  • Introduce Quarkus-specific decoupling: when the plugin version is sourced from a property also used by an imported Quarkus BOM, create/use quarkus-plugin.version instead of modifying the BOM property.
  • Emit a warning when the Quarkus platform BOM property remains behind after decoupling, and add comprehensive tests covering the new behaviors (including pluginManagement).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/PluginUpgradeStrategy.java Adds Quarkus plugin upgrade entries and implements BOM-property decoupling + version-gap warning behavior.
impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvnup/goals/PluginUpgradeStrategyTest.java Adds targeted unit tests for Quarkus plugin upgrade, decoupling scenarios, warning emission, and pluginManagement handling.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +940 to +947
private boolean decoupleQuarkusPluginVersion(
Document pomDocument,
Element pluginElement,
Element versionElement,
String sharedPropertyName,
PluginUpgradeInfo upgrade,
String sectionName,
UpgradeContext context) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method signature is actually:

private boolean decoupleQuarkusPluginVersion(
    Document pomDocument,
    Element versionElement,
    String sharedPropertyName,
    PluginUpgradeInfo upgrade,
    String sectionName,
    UpgradeContext context)

There is no pluginElement parameter — the parameter at that position is versionElement, and it IS used at line 983: editor.setTextContent(versionElement, "${" + newPropertyName + "}"). All parameters are used.

gnodet and others added 6 commits July 7, 2026 22:12
Add PluginUpgrade entries for quarkus-maven-plugin (both io.quarkus
and io.quarkus.platform groupIds) with minimum version 3.26.0.
When the plugin version references the same property as a Quarkus BOM,
the strategy decouples it by introducing a separate quarkus-plugin.version
property. After upgrading, emits a warning if the platform BOM version
is significantly older than the plugin version.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…maven.config

mvnup inherits the Maven launcher's argument handling, which appends
options from .mvn/maven.config. That file often contains build flags
like -ntp, -U, or -T that mvnup does not recognize, causing a
ParseException before any upgrades are applied.

Override CLIManager.parse() to catch UnrecognizedOptionException and
retry after removing the offending option, so mvnup works in projects
with a .mvn/maven.config file.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
gmavenplus-plugin (all versions up to 4.1.1) calls mutating methods
on immutable lists returned by the Maven 4 API, causing
UnsupportedOperationException on goals like removeStubs.

Add a warning-only check to CompatibilityFixStrategy that detects
known incompatible plugins and emits a warning with a link to the
upstream issue tracker. The check does not modify the POM.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Maven 4's prefix-based artifact filtering may block resolution from
repositories that do not publish a prefixes.txt file. When Maven 4
auto-generates an incomplete prefix list, legitimate artifacts are
rejected with ArtifactFilteredOutException.

Add a warning for non-Maven-Central repositories detected in POMs,
advising users to check prefix configuration if builds fail.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Maven 4 validates module paths during POM parsing, before property
interpolation occurs. Paths like <module>spark-${spark.version}</module>
are rejected because the literal string (with the unresolved
expression) does not correspond to an existing directory.

Add a warning for module/subproject elements containing ${...}
expressions, both at root level and inside profiles.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…cy versions

In CI-friendly projects using ${revision}, ${sha1}, or ${changelist}
as the parent version, child modules with versionless dependencies
may fail with 'dependencies.dependency.version is missing' because
Maven 4 validates dependency completeness before fully resolving the
parent's dependencyManagement chain.

Add a warning that detects this pattern: a parent with a CI-friendly
version expression and dependencies without explicit <version> elements.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gnodet gnodet force-pushed the fix-https-github-com-apache-maven-issues-12429 branch from a431530 to df2ec27 Compare July 7, 2026 22:34
@gnodet gnodet changed the title Fix #12429: Add quarkus-maven-plugin upgrade support to mvnup mvnup: Quarkus plugin upgrade, CLI robustness, and Maven 4 compatibility warnings Jul 7, 2026
@gnodet gnodet requested a review from Copilot July 7, 2026 23:21

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Comment on lines +159 to +175
@Override
public CommandLine parse(String[] args) throws ParseException {
List<String> currentArgs = new ArrayList<>(List.of(args));
Set<String> removed = new HashSet<>();
while (true) {
try {
return super.parse(currentArgs.toArray(new String[0]));
} catch (UnrecognizedOptionException e) {
String badOption = e.getOption();
if (removed.contains(badOption) || !currentArgs.remove(badOption)) {
// Already tried removing this option or can't find it — give up
throw e;
}
removed.add(badOption);
}
}
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! Fixed in 7444c4d. The retry loop now also removes the trailing argument value when the next token doesn't start with -. For example, -T 4 (two tokens) now correctly removes both instead of leaving 4 as a spurious goal in getArgList().

Added a test testUnrecognizedOptionWithSeparateArgument that verifies -T 4 doesn't leak into the goals list.

Comment on lines +961 to +965
if (currentVersion != null && !isVersionBelow(currentVersion, upgrade.minVersion)) {
context.debug("Quarkus plugin version (via shared property " + sharedPropertyName + ") " + currentVersion
+ " is already >= " + upgrade.minVersion);
return false;
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! Fixed in 7444c4d. When currentVersion is null (property not declared in the current POM, likely inherited from parent), the method now returns false immediately instead of falling through and unconditionally introducing quarkus-plugin.version=3.26.0.

This prevents an accidental downgrade when the inherited property already resolves to a version >= 3.26.0.

Added a test shouldNotDecoupleWhenSharedPropertyIsInherited that verifies no quarkus-plugin.version property is introduced when the shared property is only in the parent POM.

Comment on lines +930 to +940
if (url != null && !url.contains("${")) {
String normalizedUrl = url.trim().replaceAll("/+$", "");
if (!WELL_KNOWN_REPO_URLS.contains(normalizedUrl)) {
context.warning("Repository '" + (id != null ? id : normalizedUrl)
+ "' is a third-party repository. Maven 4's prefix-based artifact"
+ " filtering may block resolution if the repository does not"
+ " publish a prefixes.txt file. If builds fail with"
+ " 'ArtifactFilteredOutException', check repository"
+ " prefix configuration.");
}
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed — this warning was too aggressive. After investigating the actual Maven 4 behavior, the prefix filter defaults to permissive when no prefixes.txt exists (noInputOutcome=true). Repositories without a prefix index are not blocked at all.

The entire #12433 warning has been reverted in e1642cf. The real issue is narrower (auto-discovered prefix files with incomplete content), and Maven already provides per-repo disable options and helpful hints in ArtifactFilteredOutException messages.

Maven 4's prefix filtering defaults to permissive behavior when a
repository has no prefixes.txt — artifacts are allowed through, not
blocked. The warning was based on a misunderstanding of the default
noInputOutcome=true setting. The actual issue is narrower: only
auto-discovered prefix files with incomplete content can cause false
negatives, and Maven already provides per-repository disable options
and helpful hints in ArtifactFilteredOutException messages.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gnodet gnodet marked this pull request as ready for review July 8, 2026 06:12
…rty guard

- CLI option stripping: also remove trailing argument values for
  options like "-T 4" (two tokens) to prevent the value from being
  treated as a spurious goal.
- BOM property decoupling: skip when the shared property is not
  declared in the current POM (inherited from parent), since we
  cannot resolve its actual value and introducing a new property
  could downgrade an already-sufficient inherited version.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gnodet

gnodet commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

In-Depth PR Review

I've reviewed all assertions in this PR about Maven 4 behavior, checked upstream sources, and verified edge cases. Here's a detailed analysis.


🔴 Critical: gmavenplus-plugin claim is factually incorrect

The KNOWN_INCOMPATIBLE_PLUGINS entry states:

"Even the latest version (4.1.1) fails with UnsupportedOperationException on goals like removeStubs."

This is wrong. The latest version is 5.1.0 (released 2026-07-02), and the Maven 4 fix was merged in 4.2.0 — via groovy/GMavenPlus#328, authored by @gnodet. The 4.2.1 release notes explicitly say "Support Maven 4 (#328) Thanks to @gnodet for this PR!"

Additionally, the Maven-side issue MNG-8657 was also fixed (resolved 2025-05-27) via MNG-8662 which added the missing removeTestCompileSourceRoot method.

A follow-up issue #341 about iterator.remove() deprecation warnings was also fixed and closed (2026-06-24).

Recommendation: Convert gmavenplus-plugin from a "permanently incompatible" warning to a PluginUpgrade with minimum version 4.2.0 (or 5.0.0 if you want to skip the deprecation warning era). The current code misleads users into thinking there's no fix when one exists — and was contributed by the PR author.


🟡 Questionable: "3.31.x ceiling" claim is unsubstantiated

The Javadoc on emitVersionGapWarning states:

"The plugin can be bumped ahead of the platform up to 3.31.x; versions >= 3.32.0 are binary-incompatible with older platform/runtime libraries."

And the warning message tells users:

"the plugin cannot be upgraded beyond 3.31.x without a corresponding platform upgrade"

I could not verify this claim:

  1. No SerializedApplication changes between 3.31 and 3.32gh api repos/quarkusio/quarkus/compare/3.31...3.32 shows no changes to SerializedApplication.java.
  2. No upstream issue or discussion documents this boundary — I searched GitHub issues, migration guides (3.32, 3.33), and mailing lists.
  3. PR #52224 (aot-jar packaging, milestoned 3.32) does refactor SerializedApplication but explicitly states "This doesn't change the format, it just reorganizes the code."
  4. The Quarkus platform does enforce version alignment between plugin and BOM, but that's a general policy, not a binary incompatibility at a specific version boundary.

Recommendation: Either provide evidence for the 3.31.x ceiling or soften the warning to something like: "Consider upgrading the platform to match — mismatched plugin and platform versions may cause unexpected behavior." A false hard boundary can lead users to avoid valid upgrades.


🟡 Wrong upstream reference in PR description

The PR body references quarkusio/quarkus#46889 — that's a WebAuthn TLS issue, not the Maven 4 fix. The correct reference is:

The minimum version 3.26.0 is correct.


🟢 Verified: Quarkus 3.26.0 minimum version

Confirmed via upstream: quarkusio/quarkus#37627 was fixed by PR #48248, milestoned 3.26.0.CR1. The fix addresses Aether API changes in Maven 4 where quarkus-maven-plugin called methods on the old resolver API.


🟢 Verified: CLI retry loop (CommonsCliUpgradeOptions)

The retry-loop approach for handling unrecognized options from .mvn/maven.config is sound. The trailing argument removal (fixed in 7444c4d) correctly handles options like -T 4.

Minor edge case to be aware of: Boolean flags (e.g., -B) followed by positional arguments (goal names) would cause the next token to be incorrectly removed if it doesn't start with -. Example: ["-B", "check"] → removes -B, then also removes check because it doesn't start with -. In practice this is unlikely since .mvn/maven.config options don't interleave with goal names, but it's worth noting. A Set<String> of known Maven boolean flags could make the heuristic more robust if this ever surfaces.


🟢 Verified: BOM property decoupling logic

The Quarkus BOM decoupling logic is correct:

  • isPropertyUsedByQuarkusBom() correctly identifies shared properties by checking dependencyManagement for io.quarkus/io.quarkus.platform BOMs with type=pom, scope=import
  • decoupleQuarkusPluginVersion() introduces quarkus-plugin.version without touching the BOM property
  • The inherited property guard (returning false when currentVersion == null, fixed in 7444c4d) prevents downgrading inherited versions

🟢 Verified: Warning checks

  • Property-interpolated module paths: Correctly identifies ${...} expressions in <module> and <subproject> elements, including profile-level modules. This is a real Maven 4 issue (module paths are validated before interpolation).
  • CI-friendly missing dependency versions: Correctly targets the pattern where a parent POM uses ${revision} and child dependencies rely on inherited dependencyManagement. Note: only checks root-level <dependencies>, not dependencies within <profiles> — a minor gap that's unlikely to matter in practice.

Summary

Check Status
Quarkus 3.26.0 minimum version ✅ Correct
CLI retry loop robustness ✅ Sound (minor edge case noted)
BOM property decoupling ✅ Correct
Property-interpolated module path warning ✅ Correct
CI-friendly version warning ✅ Correct (minor gap in profiles)
gmavenplus-plugin "incompatible" claim Wrong — fixed since 4.2.0
3.31.x ceiling claim ⚠️ Unsubstantiated — no evidence found
Quarkus PR reference in description ⚠️ Wrong issue number (#46889 → #37627)

…n version gap warning

- Move gmavenplus-plugin from KNOWN_INCOMPATIBLE_PLUGINS (permanent warning)
  to PluginUpgrade with minimum version 4.2.0. The Maven 4 incompatibility
  was fixed in groovy/GMavenPlus#328 (released in 4.2.0), and the Maven-side
  fix landed in MNG-8662.
- Soften the Quarkus version gap warning: remove the unsubstantiated claim
  about binary incompatibility at 3.32.0 and replace with general version
  alignment advice.
- Fix wrong Quarkus upstream reference in PR description (#46889 → #37627).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants