Improve standalone ApiRunner to properly apply settings - #12998
Conversation
gnodet
left a comment
There was a problem hiding this comment.
Solid enhancement that makes the standalone ApiRunner production-ready by wiring proxy, mirror, auth, and dependency resolution from settings.xml.
One item merits attention:
getMavenVersion() can still return null when pom.properties is absent from the classpath or version parsing fails, violating the @Nonnull contract declared in Session.java. The PR improves on the previous code (which always returned null in standalone mode) but doesn't fully close the gap. A fallback sentinel version (e.g. "0.0.0") would satisfy the contract.
Strengths:
- The proxy/mirror/auth configuration faithfully follows patterns from
DefaultRepositorySystemSessionFactoryandDefaultMavenExecutionRequestPopulator - Clean removal of dead code and stale TODO comments
- Widening 5
MavenSessionBuilderSuppliermethods fromprotectedtopublicis appropriate — these are in theimplmodule, not the public API
Note: The commit message uses a placeholder JIRA number ([MNG-8xxx]). This should be replaced with a real issue number before merging.
This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.
Claude Code on behalf of Guillaume Nodet
gnodet
left a comment
There was a problem hiding this comment.
Delta re-review — 3 new commits since previous review.
The three new commits are well-implemented improvements:
- Property loading (
e484175,333654f) correctly mirrors the CLI's precedence (installation system → user system → user properties) without needing${includes}directive support. The~/.m2/location for user properties is correct per Maven conventions. Code comments clearly explain the design decisions. - Repository handling (
7521131) properly preserves settings-derived repositories in mvnup and only appends fallback defaults when not already present. The set-based ID check is the right approach.
Previous findings status
| Finding | Status | Detail |
|---|---|---|
getMavenVersion() null vs @Nonnull |
⬆️ Improved | detectMavenVersion() now tries both maven-core and maven-impl pom.properties, but still returns null when neither exists. Pre-existing on master (previously always null in standalone mode), so the PR is an improvement. A fallback sentinel (e.g. VersionParser.parseVersion("0.0.0")) would fully close the gap. |
Placeholder JIRA [MNG-8xxx] |
⏳ Pending | First commit message still reads [MNG-8xxx] — should be replaced with a real issue number before merge. |
New observation (low severity)
loadMavenProperties silently swallows IOException and returns an empty map. The class has no logger, so there's nowhere to log, but this could mask real configuration failures on files that exist but are unreadable (permissions, concurrent deletion). A debug-level log or comment explaining the rationale would help.
This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.
Claude Code on behalf of Guillaume Nodet
333654f to
df53ab2
Compare
gnodet
left a comment
There was a problem hiding this comment.
Delta re-review — All previous findings addressed.
✅ getMavenVersion() null contract — detectMavenVersion() now returns @Nonnull Version, falling back to a "0.0.0" sentinel when no pom.properties is on the classpath. Fully closes the @Nonnull contract gap.
✅ Placeholder JIRA [MNG-8xxx] — Removed from the rebased commit message.
✅ Silent IOException in loadMavenProperties — Catch block now has a clear 3-line comment explaining the design rationale: properties files are optional, standalone API has no logger, and failure gives the same behavior as if the file didn't exist.
Clean fix commit, well-scoped to exactly the reported issues. No new concerns.
This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.
Claude Code on behalf of Guillaume Nodet
gnodet
left a comment
There was a problem hiding this comment.
Delta review — one new commit: 6d26d3fdec — "Support encrypted settings passwords in standalone ApiRunner"
Well-designed addition with a clean SecurityMode API, proper classpath detection, and support for both legacy ({...}) and Maven 4 master-key-based encryption.
Two minor observations (non-blocking):
-
hasExistingDispatchersguard (ApiRunner.java:252): The check usesgetAllBindings(Dispatcher.class)which only finds unqualified bindings. The test-scopedSecDispatcherProviderregisters named dispatchers (via@Named(LegacyDispatcher.NAME)etc.), so the guard doesn't actually detect them. In practice this is harmless (DI framework handles duplicates and dispatchers are stateless), but the check doesn't achieve its documented intent of preventing duplicate bindings in the test scenario. -
SecurityModetest coverage (ApiRunner.java:143): The existingTestApiStandaloneimplicitly exercises the defaultIF_AVAILABLE_WARNpath, but theNONE,IF_AVAILABLE, andREQUIREDmodes are untested. In particular, theREQUIREDmode'sMavenExceptionthrow when plexus-sec-dispatcher is absent has no test verifying the behavior.
Positive notes:
- Good design decision to call
configureSecurityDispatchersbeforeinjectorConsumer.accept(injector), allowing callers to override default bindings. SecDispatcherBindingscorrectly mirrors the test-scopedSecDispatcherProvider, covering all four master source types.- Visibility changes in
MavenSessionBuilderSupplier(protected→public) are a reasonable follow-on from earlier commits.
🤖 This review was generated by ForgeBot.
gnodet
left a comment
There was a problem hiding this comment.
Delta review — one new commit: d220f4e91c — "Add tests for ApiRunner settings handling and SecurityMode"
✅ SecurityMode test coverage — All four enum values (NONE, IF_AVAILABLE, IF_AVAILABLE_WARN, REQUIRED) are now exercised in the happy path. Settings features (servers, mirrors, repositories, offline, encrypted passwords) are well-tested with proper isolation. The REQUIRED mode's exception path when plexus-sec-dispatcher is absent remains untested, but the test comment explicitly acknowledges this limitation — the library is a compile dependency, so ClassNotFoundException cannot be triggered without classloader tricks. Reasonable trade-off.
Good use of the validated master/server password values from the mng-8379 integration test fixtures.
No new concerns.
🤖 This review was generated by ForgeBot.
The standalone ApiRunner is the main entry point for using the Maven 4 API outside of a full Maven build (e.g. in tools like mvnup). Previously it had several gaps that made it unsuitable for production use: - Hardcoded user.home to "target" (meant for unit tests only) - Settings were built but never stored on the session (getSettings() always returned empty) - No proxy, mirror, or server authentication support - Missing dependency resolution machinery (DependencySelector, DependencyGraphTransformer, DependencyManager, etc.) - getMavenVersion() returned null, violating @nonnull contract - Offline mode from settings was ignored This commit fixes all of these issues: - Remove the hardcoded user.home override so real settings.xml is loaded - Store effective settings on the session so getSettings() returns them - Apply proxy configuration from settings via DefaultProxySelector - Apply mirror configuration from settings via DefaultMirrorSelector - Apply server authentication from settings via DefaultAuthenticationSelector - Configure dependency resolution via MavenSessionBuilderSupplier (DependencyTraverser, DependencyManager, DependencySelector, DependencyGraphTransformer, ArtifactTypeRegistry, ArtifactDescriptorPolicy) - Detect and set Maven version from classpath pom.properties - Honor offline mode from settings - Clean up stale TODO comments and dead commented-out code Also makes 5 getter methods in MavenSessionBuilderSupplier public (from protected) so they can be reused by ApiRunner across packages. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ApiRunner Resolves the Maven configuration directory (via maven.installation.conf, maven.conf, or maven.home) and loads both property files with interpolation, matching the CLI behavior. System properties are merged into the session's system properties map; user properties are exposed via getUserProperties() and set on the resolver session. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The installation-level maven-user.properties (in ${maven.home}/conf/)
is a CLI bootstrapper that contains Maven-internal configuration
(maven.cache.config, aether.conflictResolver.impl) which interferes
with standalone usage. Load user properties from the user-level
location (~/.m2/maven-user.properties) instead, matching the
${includes} chain the CLI follows.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- detectMavenVersion() now returns "0.0.0" sentinel instead of null when no pom.properties is on the classpath, so Session.getMavenVersion() is never null. - loadMavenProperties() IOException catch block now has a comment explaining why the exception is silently ignored (no logger available, files are optional configuration, same behavior as if file didn't exist). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add SecurityMode enum to control how the standalone session handles
encrypted passwords in settings.xml:
- NONE: skip decryption entirely
- IF_AVAILABLE: try to bind dispatchers, skip silently if unavailable
- IF_AVAILABLE_WARN (default): try, warn if unavailable
- REQUIRED: try, fail if unavailable
When plexus-sec-dispatcher is on the classpath, both legacy ({...}) and
Maven 4 master-key-based encrypted passwords are supported. The binding
is skipped if dispatchers are already provided (e.g., by test discovery).
Remove the manual LegacyDispatcher binding from mvnup's
AbstractUpgradeStrategy since ApiRunner now handles it.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Covers all four SecurityMode values, settings servers/mirrors/repositories, legacy encrypted password decryption, plaintext password pass-through, and offline mode. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
d220f4e to
e2bc0f7
Compare
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ceTest The relocatedTarget method now declares ArtifactDescriptorException but this test method was not updated to handle it. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Backport of #12998 to maven-4.0.x. The standalone ApiRunner is the main entry point for using the Maven 4 API outside of a full Maven build (e.g. in tools like mvnup). This improves it to be production-ready: - Remove hardcoded user.home = "target" — read settings from real user home - Store effective settings on the session - Apply proxy, mirror, and server authentication from settings.xml - Configure dependency resolution machinery - Support encrypted settings passwords with SecurityMode enum - Load maven-system.properties and maven-user.properties - Detect and set Maven version from classpath pom.properties - Honor offline mode from settings - Use ApiRunner-provided repositories in mvnup instead of replacing them
Summary
The standalone
ApiRunneris the main entry point for using the Maven 4 API outside of a full Maven build (e.g. in tools like mvnup). Previously it had several gaps that made it unsuitable for production use — settings were hardcoded to a test directory, proxy/mirror/auth fromsettings.xmlwere ignored, and dependency resolution machinery was not configured.This PR fixes all of these issues:
user.home = "target"— the standalone API now reads settings from the real user home (~/.m2/settings.xml)session.getSettings()now returns the actual effective settings instead of an empty objectDefaultProxySelectorDefaultMirrorSelectorDefaultAuthenticationSelector(username, password, private key)MavenSessionBuilderSupplier—DependencyTraverser,DependencyManager,DependencySelector,DependencyGraphTransformer,ArtifactTypeRegistry,ArtifactDescriptorPolicypom.properties(fixes@Nonnullcontract violation)Also makes 5 getter methods in
MavenSessionBuilderSupplierpublic (from protected) so they can be reused byApiRunneracross packages.Test plan
maven-implpass (0 failures)TestApiStandalone— verifies artifact resolution and dependency collection workRequestTraceTest— verifies request tracing works with standalone sessionDiTest— verifies DI wiringmaven-corecompiles successfully with the visibility changes🤖 Generated with Claude Code