Add dependency:add, dependency:remove, and dependency:search goals#1599
Add dependency:add, dependency:remove, and dependency:search goals#1599brunoborges wants to merge 59 commits intoapache:masterfrom
Conversation
…cy:search goals Formal specification document proposing three new CLI-based dependency management goals for the Maven Dependency Plugin: - dependency:add — Add/update dependencies in pom.xml from the CLI - dependency:remove — Remove dependencies from pom.xml from the CLI - dependency:search — Query Maven Central for artifacts The spec covers parameter definitions, GAV shorthand format, multi-module behavior, version inference from dependencyManagement, formatting-preserving POM editing strategy, error handling, and a phased implementation plan. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Decisions: - Q1: Include -Dbom flag (BOM import shorthand) in initial scope - Q2: No inline exclusions support (manual POM editing) - Q3: No dry-run mode - Q4: Always append new dependencies to end of list - Q5: Target Maven Central Search v2 REST API only - Q6: Always insert literal version strings (no property creation) Added §3.11 BOM Support section and bom parameter to the add goal. Replaced Open Questions section with Design Decisions table. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
New Mojo goals for CLI-based dependency management: - AddDependencyMojo (dependency:add): Adds/updates dependencies in pom.xml with support for GAV shorthand, -Dmanaged, -Dbom, -Dmodule, -DupdateExisting, and version inference from dependencyManagement. - RemoveDependencyMojo (dependency:remove): Removes dependencies from pom.xml with child module safety checks when removing managed deps. - SearchDependencyMojo (dependency:search): Queries Maven Central Search API and displays results in tabular format. No project context required. Infrastructure classes: - DependencyCoordinates: GAV parsing and validation - PomEditor: DOM-based formatting-preserving POM read/write Unit tests: 34 new tests covering coordinate parsing, POM editing (add/remove/update/comments preservation/BOM), and JSON response parsing. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Bugs fixed: - #1 -Doptional=true now correctly emits <optional>true</optional> (was declared but never passed to PomEditor) - #2 XML namespace-aware parsing: PomEditor now uses setNamespaceAware(true) and localName() matching, so POMs with xmlns="http://maven.apache.org/POM/4.0.0" work correctly Robustness improvements: - apache#3 Atomic file writes: save() writes to a temp file then renames, preventing corruption from concurrent POM modifications - apache#4 GAV trailing colons: split(":", -1) preserves trailing empties; empty groupId/artifactId now rejected with clear error messages - apache#5 JSON escaped quotes: extractStringField() now handles \" and \\ in JSON string values instead of using a naive regex Formatting preservation: - apache#6 XML declaration preserved: POMs without <?xml?> no longer get one added; standalone="no" attribute no longer injected - apache#7 Indent detection improved: GCD-based analysis across all indented lines instead of using only the first indented line - apache#8 UTF-8 BOM preserved: BOM marker detected on load and re-emitted on save - apache#9 Comment cleanup on remove: preceding XML comments associated with a removed dependency are now also removed 48 tests (14 new), all passing. Full existing test suite unaffected. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…User-Agent, enriched toString - findDependency/removeDependency now match on type and classifier, preventing wrong-entry updates when multiple variants exist (e.g., jar vs test-jar, different classifiers) - Guard against null session.getProjects() in -Dmodule resolution for both AddDependencyMojo and RemoveDependencyMojo - User-Agent version derived from package manifest instead of hardcoded - DependencyCoordinates.toString() now includes scope, type, classifier - Simplified AddDependencyMojo log messages (no redundant scope append) - Added 8 new tests (56 total) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…details
- Validate Content-Type header is JSON before parsing response
- Check response body starts with '{' to detect HTML error pages
- Read error stream on non-200 responses, include in error message
- Special-case HTTP 429 (rate limit) with user-friendly message
- Truncate error body to 500 chars to keep messages readable
- Added 2 new tests (58 total)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ference
When POM dependencies use property references (e.g., ${project.groupId}),
PomEditor's literal XML matching can't find them. Both AddDependencyMojo
and RemoveDependencyMojo now cross-reference Maven's resolved model:
- dependency:add: if not found in raw XML but exists in resolved model,
blocks with a clear error (or warns and adds literal entry with
-DupdateExisting)
- dependency:remove: if not found in raw XML but exists in resolved
model, tells user to edit manually since property references can't
be safely removed automatically
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…root element check - Block DOCTYPE declarations in PomEditor to prevent entity-expansion DoS attacks; enable FEATURE_SECURE_PROCESSING - Validate root element is <project> to reject non-POM XML files - Validate scope values against Maven's known set (compile, provided, runtime, test, system, import) - Change property-interpolation cross-reference to always block instead of allowing duplicate entries with -DupdateExisting - Added 5 new tests (63 total) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…erited deps existsInResolvedModel() now queries project.getOriginalModel() instead of project.getDependencies()/getDependencyManagement(). The original model contains only what is declared in the POM (after interpolation but before inheritance merging), so inherited parent dependencies no longer incorrectly block dependency:add or cause misleading errors in dependency:remove on child modules. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
New test classes: - AddDependencyMojoTest (6 tests): NPE guard on null/empty reactor, property-interpolated dep blocks add, inherited dep doesn't block child, always-block even with updateExisting, managed section cross-reference - RemoveDependencyMojoTest (4 tests): NPE guard on null/empty reactor, property-interpolated dep shows clear error, non-existent dep gives not-found error - SearchDependencyMojoHttpTest (6 tests): HTTP 500 includes body, 429 rate-limit message, HTML-200 rejected via Content-Type, non-JSON body rejected, valid response accepted, dynamic User-Agent Total: 79 tests (was 63), all passing. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…Ts, README Documentation updates: - specification.md: Updated 11 sections to reflect all edge case fixes (XXE hardening, type/classifier matching, scope validation, property- interpolation detection, atomic writes, search error handling) - README.md: Added 'New: CLI Dependency Management Goals' section with quick-start examples - Mojo Javadoc: Fixed incorrect RemoveDependencyMojo.gav docs, enhanced all three class-level and field Javadocs - Site: New APT example page covering all 3 goals with practical examples; added menu entry to site.xml Integration tests (6 projects): - add-dependency/basic: explicit coordinates - add-dependency/gav-shorthand: GAV with scope - add-dependency/managed: dependencyManagement target - remove-dependency/basic: remove existing dependency - remove-dependency/not-found: expected failure - search-dependency/basic: search Maven Central Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Strip Unicode BOM character (U+FEFF) before checking for XML declaration; Java's trim() only removes ASCII whitespace, so BOM-prefixed POMs had their XML declaration dropped on save - Validate rows parameter is positive (>= 1) before sending to API; negative/zero values now fail fast with a clear error - Added 3 new tests (82 total) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Similar to archetype:generate, the search goal now presents results as a numbered list when a console is available. Users can: - Select an artifact by number to browse available versions - Type free text to refine the search with a new query - Press Enter to quit, or 'b' to go back from version selection After selecting an artifact and version, the goal prints ready-to-use dependency:add commands for regular, scoped, and managed dependencies. Interactive mode is enabled by default and automatically disabled when: - No console is available (piped output, IDE) - Maven runs in batch mode (-B) - User passes -Dinteractive=false Non-interactive mode preserves the original tabular output behavior. Implementation details: - performSearch() refactored to accept query/rows/gavMode parameters - Version lookup uses core=gav Solr parameter for per-version results - New extractVersionList() parses the GAV response format (v field) - readLine() is package-private and overridable for testing - 8 new tests (6 interactive + 2 extractVersionList) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Empty latestVersion no longer produces invalid GAV: falls back to first version from the GAV query; if both are unavailable, warns and returns to artifact list instead of printing broken command - Version list shows paging hint when more versions exist than the 20 returned (e.g., '... and 30 older version(s) not shown') - Support 'q' and 'quit' as quit commands at all interactive prompts, matching common TUI conventions - 5 new tests (empty latestVersion, no versions at all, q to quit, version paging hint, q from version prompt) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…pdate Fix #1: dependency:remove now has explicit -Dtype, -Dclassifier, and -Dbom parameters, matching dependency:add's parameter parity. The -Dbom flag is shorthand for -Dmanaged -Dtype=pom, making BOM removal as simple as: mvn dependency:remove -Dgav=g:a -Dbom Fix #2: dependency:add -DupdateExisting can now clear fields: - -Dscope=NONE removes the <scope> element - -Dtype=NONE removes the <type> element - -Dclassifier=NONE removes the <classifier> element - -Doptional=false removes the <optional> element The 'optional' parameter changed from boolean to Boolean so null (not specified) is distinguishable from false (explicitly clear). PomEditor.updateDependency() treats empty strings as remove signals. Fix apache#3: Interactive search re-query behavior is by design (documented in code comments) — more powerful than local filtering since it leverages the full Maven Central search index. 6 new tests, 404 total pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
buildDependencyElement now guards against empty strings for scope, type, classifier, and version fields. Previously, using -Dscope=NONE on a dependency that didn't exist would create <scope></scope> in the POM. Empty strings are now treated as absent, matching the intended behavior where NONE only removes elements during updates. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Site page (apt.vm): - Document -Dtype and -Dclassifier params for dependency:add - Document NONE sentinel for clearing fields during updates - Document scope validation (6 allowed values) - Document -Dtype, -Dclassifier, -Dbom params for dependency:remove - Add q/quit and b/back interactive commands - Fix output text: 'result found' -> 'result(s) found' - Remove incorrect -B batch mode claim Specification: - Fix comment cleanup: 'no blank line gap' -> contiguous nodes - Fix interactive prompts to match actual code text - Fix interactive commands: add q/quit at all prompts, b/back - Remove batch mode (-B) claim from interactive parameter Javadoc: - Fix SearchDependencyMojo interactive field docs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
dependency:add and dependency:remove now support a -Dprofile parameter to target dependencies within a specific Maven profile's <dependencies> or <dependencyManagement> section. If the profile does not exist, it is created automatically with the specified <id>. PomEditor changes: - Added profileId field with setProfileId() setter - getDependenciesElement() navigates into <profiles><profile> when set - findOrCreateProfile() finds existing or creates new profile by <id> - Depth calculations offset by +2 when inside a profile 5 new tests: add to new profile, add to existing profile, remove from profile, isolation from top-level, profile-not-found returns null. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The -Dprofile parameter now fails with MojoFailureException if the specified profile is not found in the POM. This prevents the plugin from becoming a general POM editing tool. PomEditor.findOrCreateProfile replaced with public findProfile() (find-only). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Tests that AddDependencyMojo and RemoveDependencyMojo throw MojoFailureException with a clear message when -Dprofile targets a profile that does not exist in the POM. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
New tests: - AddDependencyMojoTest: no-profiles-section failure, happy-path add - RemoveDependencyMojoTest: no-profiles-section failure, happy-path remove - PomEditorTest: profile + managed (dependencyManagement in profile) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
New tests: - basicAddWithGavShorthand: GAV parsing through full mojo flow - duplicateDependencyWithoutUpdateExistingFails: error on duplicate - updateExistingChangesVersion: happy-path update flow - bomFlagSetsTypeAndScopeAndManaged: -Dbom sets pom/import/managed - managedWithoutVersionFails: version required for dependencyManagement Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Moved resolveTargetProject(), existsInResolvedModel(), and getModuleNames() from AddDependencyMojo and RemoveDependencyMojo into the shared AbstractDependencyMojo base class. This eliminates ~40 lines of identical duplicated code and ensures consistent behavior (e.g., RemoveDependencyMojo now includes available modules in its error message, matching AddDependencyMojo). Also cleaned up unused imports in both mojos. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove dead code: performSearch() no-arg overload, getManagementKey() - Add @SInCE 3.11.0 to DependencyCoordinates and PomEditor - Extract shared calculateColumnWidths() and buildSeparator() helpers in SearchDependencyMojo to deduplicate table rendering logic - Fix detectIndent() to handle bare CR line endings - Trim module parameter in resolveTargetProject() - Fix displayResults() to skip GAV suggestion when version is empty - Fix misleading 'atomic write' comment in PomEditor.save() Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Spec fixes: - GAV footnote: clarify that explicit -D params override gav fields - Remove not-found message: section varies by target (not hardcoded) - Safety warning: remove 'Warning:' prefix to match actual output - DependencyInserter reference: replaced with PomEditor (never existed) - Search API decision: 'Solr-compatible REST API' (not 'newer REST') - rows parameter: document >= 1 validation Code fix: - handleBomFlag() now clears classifier (BOM imports must not have one) New tests (14): - Add: version inferred from parent dependencyManagement (critical) - Add: parent POM warns about inheritance to child modules - Add: missing groupId+artifactId fails with clear message - Add: malformed gav fails - Add: -Dbom clears classifier - Add: explicit -D params override gav fields - Remove: managed removal warns about child modules (critical) - Remove: missing groupId+artifactId fails - Remove: malformed gav fails - Remove: not-found in dependencyManagement shows correct section - Search: empty/blank query fails - Search: rows=0 fails with positive integer message - PomEditor: remove returns false when section is absent - PomEditor: updateDependency sets type, classifier, and optional Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Spec fixes: - Managed-version output: correct log order and bracket format - Search result count: 'result(s)' matches code - Error messages: include full text with context details New tests (3): - Add: successful module targeting via reactor - Add: NONE sentinel clears scope on update - Remove: successful module targeting via reactor Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The custom -Dmodule parameter for targeting child modules is redundant with Maven's built-in -pl (project list) reactor flag. Using -pl is more idiomatic and consistent with how Maven users already work with multi-module projects. Changes: - Remove module field from AddDependencyMojo and RemoveDependencyMojo - Remove resolveTargetProject() and getModuleNames() from AbstractDependencyMojo - Update specification.md to document -pl usage instead of -Dmodule - Update site documentation (cli-dependency-management.apt.vm) - Remove 6 module-targeting tests (moduleWithNullReactor, moduleWithEmptyReactor, moduleTargetingResolvesCorrectProject) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep the file locally for reference but exclude it from the repository. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Third audit fixes (5b43f82)Fixed:
All 389 tests pass. |
Real-world integration testingInstalled the plugin locally ( Projects tested
Results: 30/30 pass (1 cosmetic observation)
Key findings
One cosmetic observationWhen |
I'm not aware of anything that DomTrip would lost at the moment. AFAIK, as long as the XML is syntactically parseable, everything is fully preserved. |
Auto-detect managed dependency and version property patterns from
existing dependencies. When align=true (the default), dependency:add
follows the project's conventions:
- Managed deps: if most deps are version-less, add managed dep to parent
- Version properties: if versions use ${...} refs, create a property
- Property naming: detect .version, -version, Version, version. patterns
- Multi-module: walk parent chain, add managed dep + property to parent
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
How are we feeling about this now? |
@brunoborges Could you have a look at https://github.com/brunoborges/maven-dependency-plugin/pull/2/changes ? |
Add convention auto-detection for dependency:add (align mode)
- Replace string-based pattern encoding with PropertyPattern enum - Add existsInResolvedModel() check in addCrossPom() to prevent duplicates when dependencies use property interpolation - Add unit tests for PropertyPattern, Conventions, and detectPropertyPattern() covering all patterns, edge cases, and defaults Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
PR description has been updated to reflect the latest state of the proposed implementation, including the new convention auto-detection feature ( |
|
@gnodet and @rmannibucau PR up to date with all feedback given. |
|
@rmannibucau time to merge? |
Wdyt pushing this through this week? |
|
@gnodet @cstamas what's the plan with maveniverse work overlapping with that? @brunoborges is the CI report outdated or did you miss it? 🤔 |
|
I missed it! Fixing tonight. Update coming tomorrow! |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Updated, @rmannibucau |
|
If you mean Pilot, it is not overlapping IMHO. Or wdym? |
|
Hey all, any update? |
Address review feedback from slawekjaranowski: - Replace fragile string contains() checks with proper XmlSlurper XML parsing for structured assertions - Fix version mismatch: verify scripts now check for 3.17.0 to match the version passed in invoker.properties (was incorrectly 2.10.1) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Hey @slawekjaranowski @rmannibucau @gnodet ... Are we good with pushing this through? |
| protected void doExecute() throws MojoExecutionException { | ||
| try { | ||
| // ideally this should either be DependencyCoordinates or DependencyNode | ||
| // ideally this should either be DependencyEntry or DependencyNode |
| * This catches dependencies declared with property references like {@code ${project.groupId}} | ||
| * without false-positiving on inherited dependencies from a parent POM. | ||
| */ | ||
| protected static boolean existsInResolvedModel(MavenProject project, DependencyEntry coords, boolean managed) { |
There was a problem hiding this comment.
I would not like to add a static method to abstract class when it is only used in two specific child ....
I don't have a good idea for it ... next abstract in level or util class ...
If no better idea can be as is.
Summary
This PR adds three new goals to the Maven Dependency Plugin:
dependency:add,dependency:remove, anddependency:search. These goals bring Maven's CLI experience closer to what npm, Cargo, pip, and NuGet offer — a single command to manage dependencies without manually editing XML.Motivation
Every major dependency management ecosystem provides a single-command way to add a dependency:
pip install google-adknpm install @google/adkcargo add google-adkdotnet add package Google.AdkThis friction is a barrier for developers coming from other ecosystems, and for AI coding agents where a CLI invocation is not only significantly cheaper (in token usage) than instructing an XML edit, but more importantly safer and guaranteed — different models and agent harnesses produce inconsistent results when manipulating pom.xml directly.
New Goals
dependency:addAdds a dependency to the project's
pom.xml:Features:
-DgroupId/-DartifactId/-Dversionparameters-Dmanagedflag for<dependencyManagement>section-Dbomshorthand for BOM imports (type=pom,scope=import)-Dprofileto target a specific Maven profile (profile must already exist)-plflag to target a specific child module in multi-module projectscargo add) — no flag needed<dependencyManagement>NONEsentinel to clear scope/type/classifier during updatesConvention auto-detection (
-Dalign, enabled by default)When adding a dependency, the goal automatically detects and follows the project's existing dependency management conventions:
<dependencies>entries are version-less (delegated to<dependencyManagement>), the new dependency is added the same way — managed dep to the nearest parent POM, version-less dep to the child${...}property references, a version property is automatically created.version(e.g.,guava.version),-version(e.g.,guava-version),Version(camelCase), andversion.prefix patternsaligntrueusePropertypropertyNameExplicit flags (
managed,useProperty,propertyName,align=false) always override detected conventions.Example — in a multi-module project where the parent POM uses
-versionsuffix properties:Automatically produces:
<guava-version>33.0.0-jre</guava-version>property + managed dep with${guava-version}<dependency>entrydependency:removeRemoves a dependency from the project's
pom.xml:Features:
dependency:add(-Dmanaged,-Dprofile,-Dbom)-plflag to target a specific child moduledependency:searchQueries Maven Central for artifacts:
Features:
mvn dependency:addcommandsg:com.google.adk,a:google-adk)Implementation Details
dependency:treeAbstractDependencyMojo: Add and Remove goals use constructor injection and follow the existingdoExecute()pattern. Search extendsAbstractMojodirectly (requiresProject=false).PropertyPatternenum for type-safe pattern matching; convention analysis reusesPomEditorto scan both child and parent POMsTesting
Documentation
index.apt.vmwith descriptions and linkssrc/site/apt/examples/managing-dependencies.apt.vmsrc/site/apt/usage.apt.vmupdated with Managing Dependencies section@since 3.11.0annotations on all new classesChecklist
Following this checklist to help us incorporate your contribution quickly and easily:
mvn verifyto make sure basic checks pass. A more thorough check will be performed on your pull request automatically.mvn -Prun-its verify).To make clear that you license your contribution under the Apache License Version 2.0, January 2004 you have to acknowledge this by using the following check-box.