Skip to content

Feat: Add id attribute (gav) support to Dependency, Exclusion, Mixin - #11904

Open
rbygrave wants to merge 18 commits into
apache:masterfrom
rbygrave:feature/dependency-id-attribute
Open

Feat: Add id attribute (gav) support to Dependency, Exclusion, Mixin#11904
rbygrave wants to merge 18 commits into
apache:masterfrom
rbygrave:feature/dependency-id-attribute

Conversation

@rbygrave

@rbygrave rbygrave commented Apr 8, 2026

Copy link
Copy Markdown

Based on design discussion on Issue #11500, the proposal is to support a compact id attribute format for Dependency, Exclusion and Mixin elements.

Dependency id Format

The id attribute supports the following compact formats, using the standard Maven coordinate order (groupId:artifactId:type:classifier:version):

Format Description
groupId:artifactId version managed via <dependencyManagement>
groupId:artifactId:version explicit version
groupId:artifactId:type:version with explicit type
groupId:artifactId:type:classifier:version with type and classifier

Full syntax: groupId:artifactId[[:type[:classifier]]:version][@scope][?]

A trailing : (empty version) indicates the version is managed (e.g. groupId:artifactId:type:).

Any segment may be left empty to skip setting that field, leaving it for later inference (e.g. from dependencyManagement). For example, :artifactId leaves groupId unset, and :artifactId:version leaves groupId unset with an explicit version.

An optional @scope suffix sets the dependency scope (test, provided, runtime, compile, system, import). A trailing ? marks the dependency as optional.

Examples

<!-- Basic GAV -->
<dependency id="org.slf4j:slf4j-api:2.0.17"/>

<!-- Managed version (from dependencyManagement) -->
<dependency id="org.slf4j:slf4j-api"/>

<!-- Inferred groupId (from dependencyManagement) -->
<dependency id=":slf4j-api"/>

<!-- Inferred groupId with explicit version -->
<dependency id=":slf4j-api:2.0.17"/>

<!-- With scope -->
<dependency id="org.junit.jupiter:junit-jupiter-api:5.14.1@test"/>

<!-- Inferred groupId with scope -->
<dependency id=":junit-jupiter-api@test"/>

<!-- With optional marker -->
<dependency id="commons-io:commons-io:2.11.0?"/>

<!-- Scope + optional combined -->
<dependency id="org.apache.maven:maven-core:3.9.0@provided?"/>

<!-- Import scope (for BOMs) -->
<dependency id="org.junit:junit-bom:5.12.0@import"/>

<!-- With type -->
<dependency id="org.example:lib-b:pom:1.0"/>

<!-- With type and classifier -->
<dependency id="org.example:lib-c:jar:sources:1.0"/>

<!-- Type with managed version -->
<dependency id="org.example:lib-b:pom:"/>

<!-- Exclusions with id attribute -->
<dependency id="org.postgresql:postgresql:42.7.3">
  <exclusions>
    <exclusion id="*:*"/>
  </exclusions>
</dependency>

<!-- Mixin with compact format -->
<mixin id="com.example.mixins:java-mixin:1.0.0"/>

Validation

  • When id is used, child elements groupId, artifactId, and version must not be specified (even if the corresponding segment in the id is empty).
  • type conflict is only checked when the id has 4+ parts (3+ colons, i.e. type is encoded).
  • classifier conflict is only checked when the id has 5 parts (4 colons, i.e. classifier is encoded).
  • When @scope is present in the id, a <scope> child element must not be specified.
  • When ? is present, an <optional> child element must not be specified.
  • Scope and optional can still be specified as child elements when not encoded in the id attribute.

Inference

Empty segments are skipped during normalization, leaving the corresponding field unset for later inference. This allows patterns like:

  • :artifactId — groupId and version left unset for inference from dependencyManagement
  • :artifactId:version — groupId left unset
  • groupId:artifactId:type: — version left unset (managed), type explicit

Notes

  • Mixin has the XML attribute id but uses gav underneath as the Java field, to work around a name clash with getId() from Parent.
  • Normalization splits the id into component fields and clears the id attribute, so downstream code works with standard dependency fields.
  • Existing child element values are not overridden — the id attribute only fills in fields that are currently null/empty.

Following this checklist to help us incorporate your
contribution quickly and easily:

  • Your pull request should address just one issue, without pulling in other changes.
  • Write a pull request description that is detailed enough to understand what the pull request does, how, and why.
  • Each commit in the pull request should have a meaningful subject line and body.
    Note that commits might be squashed by a maintainer on merge.
  • Write unit tests that match behavioral changes, where the tests fail if the changes to the runtime are not applied.
    This may not always be possible but is a best-practice.
  • Run mvn verify to make sure basic checks pass.
    A more thorough check will be performed on your pull request automatically.
  • You have run the Core IT successfully.

If your pull request is about ~20 lines of code you don't need to sign an
Individual Contributor License Agreement if you are unsure
please ask on the developers list.

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.

rbygrave added 2 commits April 8, 2026 18:08
Based on design discussion on Issue apache#11500, the proposal is to support
`groupId:artifactId:version` format for Dependency, Exclusion and Mixin.

This means that we can define dependencies in the form:
```xml
<dependency id="groupId:artifactId:version" />
```

Examples:
```xml
<dependency id="org.slf4j:slf4j-api:2.0.17"/>
```
```xml
<dependency id="org.postgresql:postgresql:42.7.3">
  <exclusions>
    <exclusion id="*:*"/>
  </exclusions>
</dependency>
```
```xml
<mixin id="com.example.mixins:java-mixin:1.0.0" />
```
Add more tests and make them more specific to the validation that is
being tested by each test.
<superClass>Parent</superClass>
<fields>
<field xml.attribute="true" xml.tagName="id">
<name>gav</name>

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Using field name gav with attribute name id ... in order to avoid the conflict with the existing Parent.getId()

@gnodet gnodet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Claude Code on behalf of Guillaume Nodet

Overall a clean implementation. The strict validation, test coverage, and the Mixin gav field workaround are well thought out. A few issues to address:

  1. Dependency management dependencies are not expanded -- mergeDuplicates only expands id on model.getDependencies() but not on model.getDependencyManagement().getDependencies(), nor on profile-scoped dependencies/dependency-management. The validator correctly validates all of these (lines 536-596 of DefaultModelValidator), so managed deps with id will pass validation but never get expanded.

  2. Normalization runs after raw validation -- validateRawModel runs in doReadRawModel() (line 1818) before mergeDuplicates (line 1362 in readInputModel). The validator's duplicate-detection key logic (using dependency.getId() directly when set) handles this correctly, but it means the validator sees the unexpanded state. This is fine for the conflict checks but worth keeping in mind.

  3. Dependency.getId() naming clash -- The Dependency class now has a new id field with getId()/setId() via the model generator. But Parent (and therefore Mixin) has a computed getId() method in a codeSegment that returns groupId:artifactId:pom:version. Since Mixin extends Parent, the new field was wisely renamed to gav. However, does Dependency have a similar computed getId() method anywhere? If so, the new id field's generated getter could shadow or conflict with it. Worth verifying the generated code compiles cleanly.

  4. isBlank does not trim -- The isBlank helper in the normalizer only checks for null/empty, not for whitespace-only strings. The name isBlank is misleading since String.isBlank() checks whitespace. Consider renaming to isNullOrEmpty or using String.isBlank() which handles whitespace in coordinates (e.g., " ").

}
return builder.build();
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit: this method name is misleading -- String.isBlank() in Java checks for whitespace-only strings, but this only checks null/empty. Consider renaming to isNullOrEmpty to avoid confusion, or use s == null || s.isBlank() to also handle whitespace-only values in coordinates.

Suggested change
private static boolean isNullOrEmpty(String s) {
return s == null || s.isEmpty();
}

if (dependency.getId() != null && !dependency.getId().isEmpty()) {
key = dependency.getId();
} else {
key = dependency.getManagementKey();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Good approach using dependency.getId() as the dedup key when set, since normalization hasn't run yet at this point. However, two dependencies with the same id attribute but different scopes/classifiers would collide here (since the key is just the raw id string, not the management key). After normalization, they'd have distinct management keys (which includes type and classifier). Is this the intended behavior?

@gnodet gnodet added this to the 4.1.0 milestone Jun 16, 2026
@gnodet gnodet added mvn4 enhancement New feature or request labels Jun 22, 2026
gnodet and others added 11 commits June 22, 2026 20:17
… profiles

- Expand id attributes on dependencies in dependencyManagement, not just
  top-level dependencies
- Expand id attributes on dependencies within profile sections (both
  regular dependencies and profile dependencyManagement)
- Extract expandAndDeduplicateDependencies() and
  expandProfileDependencyIds() helper methods
- Rename isBlank to isNullOrEmpty for clarity
- Add tests for dependency management and profile id expansion

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add compact `id` XML attribute to Dependency, Exclusion, and Mixin
elements in the Maven model (4.2.0+). Supported formats:
- Dependency: g:a:v, g:a:type:v, g:a:type:classifier:v
- Exclusion: g:a
- Mixin (as `gav`/`id`): g:a:v

The normalizer expands `id` into individual fields and clears it so
consumer POMs never contain the attribute. Uses forceCopy=true in
builders to ensure `id(null)` actually clears the field.

Includes comprehensive unit tests (normalizer, validator), consumer
POM transformation tests, and integration test verifying no id
attributes leak into deployed POMs.

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

Add DependencyIdStrategy that migrates verbose dependency/exclusion
child elements into compact `id` attributes when upgrading to model
version 4.2.0 via mvnup.

The strategy runs at @priority(25), after InferenceStrategy (30), so
only dependencies that still have full explicit coordinates after
inference are collapsed. Triggered by --model-version 4.2.0 or --all.

Supported transformations:
- <dependency> g:a:v / g:a:type:v / g:a:type:classifier:v
- <exclusion> g:a
- Processes all sections: dependencies, dependencyManagement, profiles,
  plugin dependencies

Inferred ids (partial coordinates like `:a` or `g:a` without version)
are intentionally not supported — the `id` attribute is a shorthand
for explicit coordinates, not a replacement for Maven's inference
mechanism. Dependencies with missing coordinates after inference are
left in their expanded form.

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

Add support for 2-part `g:a` format and trailing-colon formats
(`g:a:`, `g:a:type:`, `g:a:type:classifier:`) for dependencies
whose version is provided by dependencyManagement. The trailing
colon convention signals that version is managed, not missing.

Leave aside inferred ids (`:a:` with inferred groupId) for now.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extend the compact id attribute format from groupId:artifactId:version
to groupId:artifactId:version[@scope][?], where @scope sets the
dependency scope and trailing ? marks the dependency as optional.

Examples:
- org.junit:junit-jupiter-api:5.0@test
- commons-io:commons-io:2.11.0?
- org.apache.maven:maven-core:3.9.0@provided?

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ature/dependency-id-attribute

Combines type/classifier/managed-version support with @scope/? optional
markers in the dependency id attribute compact syntax.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Empty segments in the compact id attribute are now skipped rather than
setting fields to empty strings, allowing groupId/version/type to be
left unset for later inference from dependencyManagement.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The formal syntax incorrectly showed version before type. Maven
consistently uses groupId:artifactId:type:classifier:version order
(as in Artifact.key() and Dependency.getManagementKey()).

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

- Remove redundant model version guard in DependencyIdStrategy (the second
  check alone suffices)
- Simplify redundant type removal branches in collapseDependency()
- Remove redundant validateExclusionIdAttribute call from effective model
  validation (id is already cleared by normalizer before effective validation)

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

@gnodet gnodet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review of this large PR (3100+ lines, 42 files) adding compact id attribute syntax for Dependency, Exclusion, and Mixin elements in Maven 4.2.0.

Confirmed finding:

  1. CI broken — integration test compilation error (high): MavenITgh11904DependencyIdAttributeTest at line 44 calls super("[4.0.0-rc-3-SNAPSHOT,)") but AbstractMavenIntegrationTestCase only has a no-arg constructor. All 9 integration-test CI jobs fail with this compilation error. The fix is straightforward — use the no-arg super call. This is likely due to the PR branch being stale relative to recent changes in the AbstractMavenIntegrationTestCase base class.

Previous review feedback addressed:

  • dependencyManagement and profile dependencies are now expanded ✅
  • isBlank renamed to isNullOrEmpty

Positive observations:

  • Test coverage is strong: unit tests for the normalizer (577 lines), validator (~150 new lines with 26 validation XML files), consumer POM transformation test, integration test, StAX reader tests, and mvnup strategy tests (967 lines)
  • The forceCopy=true parameter in builders is correctly used to ensure id(null) clears the field rather than being treated as "no change"
  • The PR is large but the scope is well-defined and cohesive — splitting would risk inconsistent intermediate states
  • The core feature (compact id attribute) is well-designed and consistent with XML attribute conventions

Findings dropped after verification (6 false positives):

  • Scope/optional asymmetry in DependencyIdStrategy: intentional design, explicitly tested
  • Dedup key format mismatch: validator prohibits mixing id and expanded forms
  • Dead code in switch default: defensive coding practice
  • Duplicate isNullOrEmpty helpers: trivially private, not worth extracting
  • Mixin gav/id naming: documented Modello convention
  • Invalid id persisting through normalization: intentional for validator to catch

The main blocker is the CI compilation error — once that's fixed, the PR looks ready for a closer look at merge readiness.

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
gnodet previously requested changes Jul 24, 2026

@gnodet gnodet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Well-structured PR adding compact id attribute support for Dependency, Exclusion, and Mixin. The normalizer, validator, and mvnup strategy changes are sound. However, the integration test has a compilation error that breaks all 9 CI integration-test jobs.

All findings independently verified (4/4 confirmed):

  1. IT compilation error (high)MavenITgh11904DependencyIdAttributeTest calls super("[4.0.0-rc-3-SNAPSHOT,)") but AbstractMavenIntegrationTestCase only has a no-arg constructor (the String constructor was removed in commit be9541cb9d). CI logs confirm all 9 integration-test jobs fail.
  2. Missing mixin validation tests (medium) — ~48 lines of mixin id/gav validation logic (format check, child element conflicts) are added to DefaultModelValidator but DefaultModelValidatorTest has no mixin-related test cases. The normalizer and parser tests cover expansion/parsing, but validator error paths are untested.
  3. Dedup key inconsistency (medium, low impact) — With id set, the dedup key includes version (g:a:1.0); without id, the key is getManagementKey() (no version). Practical impact is low since the normalizer properly deduplicates after expansion.
  4. Duplicate isNullOrEmpty (low) — Identical private methods in DefaultModelNormalizer and DefaultModelValidator. Trivial; noted for completeness.

This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.

Claude Code on behalf of gnodet

class MavenITgh11904DependencyIdAttributeTest extends AbstractMavenIntegrationTestCase {

MavenITgh11904DependencyIdAttributeTest() {
super("[4.0.0-rc-3-SNAPSHOT,)");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

AbstractMavenIntegrationTestCase only has a no-arg constructor (the super(String) pattern was removed in commit be9541cb9d). This causes a compilation error that breaks all 9 integration-test CI jobs.

Suggested change
super("[4.0.0-rc-3-SNAPSHOT,)");
MavenITgh11904DependencyIdAttributeTest() {}

- Resolve merge conflicts with latest master
- Remove version-range constructor from IT (master uses no-arg)
- Add dependency/exclusion id attribute expansion in consumer POM
  builder to ensure id attributes are expanded to individual GAV
  fields before writing, matching the normalization performed
  during model building
The expandAndDeduplicateDependencies method was incorrectly applied to
dependencyManagement sections, causing a regression in
MavenITmng4403LenientDependencyPomParsingTest. That test relies on
Maven tolerating duplicate dependency declarations in dependencyManagement
of dependency POMs (per MNG-4005 lenient parsing behavior).

The deduplication of dependencies (for backward compat with Maven 2.x
lenient parsing) only applies to model.getDependencies(), not to
dependencyManagement. Extract a dedicated expandDependencyIds() method
that only expands id attributes without deduplication, and use it for
dependencyManagement and profile dependencyManagement sections.

@gnodet gnodet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Fix pushed: commit 1cd2408a68 addresses the MavenITmng4403LenientDependencyPomParsingTest regression.

Root cause: expandAndDeduplicateDependencies was applied to dependencyManagement sections, inadvertently deduplicating them. The MNG-4403 test relies on Maven tolerating duplicate <dependency> declarations in dependencyManagement of dependency POMs (MNG-4005 lenient parsing). The deduplication of getDependencies() is a backward-compat behavior inherited from Maven 2.x, but it was never applied to dependencyManagement.

Fix: extracted a dedicated expandDependencyIds() method that only expands id attributes without deduplication, and uses it for dependencyManagement and profile dependencyManagement sections. The expandAndDeduplicateDependencies method is now only used for model.getDependencies() as before.

@gnodet gnodet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Re-review after latest push (commits f585904284 and 1cd2408a68).

Previous findings status:

  1. ✅ IT compilation error — fixed (no-arg constructor in f585904284)
  2. ⚠️ Missing mixin validation tests — still no test coverage for the ~48 lines of mixin id/gav validation in DefaultModelValidator (format check, child element conflicts at lines 376-428). Not a blocker but a gap.
  3. ℹ️ Dedup key inconsistency — acknowledged, low impact
  4. ℹ️ Duplicate isNullOrEmpty — now in 3 files (normalizer, validator, consumer POM builder)

New findings from latest push:

Confirmed (1/1):

  1. Type-check divergence between normalizer and consumer POM builder (medium)DefaultModelNormalizer.expandDependencyId() uses isNullOrEmptyOrDefault(d.getType()) for the type field in 4-part and 5-part ids (treating "jar" as empty/default), but the duplicated code in DefaultConsumerPomBuilder.expandSingleDependencyId() uses plain isNullOrEmpty(d.getType()). This means the consumer POM builder won't set the type from id if the dependency already has type="jar" explicitly set — while the normalizer would. In practice this is likely harmless (jar is the default), but it's a subtle behavioral difference between two implementations that are supposed to mirror each other. Worth harmonizing.

Observation (not blocking):

The ~190 lines of id expansion logic in DefaultConsumerPomBuilder is a near-verbatim copy of the normalizer's code. This is intentional (the consumer POM builder operates on an already-built model and needs its own expansion path), but it creates a maintenance risk: any future change to the id syntax parsing needs to be applied in two places. A shared utility method or a static helper class could reduce this. Not blocking for this PR, but worth considering for follow-up.

Latest fix assessment:

Commit 1cd2408a68 correctly separates expandDependencyIds() (id expansion only) from expandAndDeduplicateDependencies() (id expansion + deduplication). The deduplication is a Maven 2.x compat behavior that only applies to model.getDependencies(), not to dependencyManagement. The fix is clean and well-scoped — the MavenITmng4403LenientDependencyPomParsingTest regression is addressed.

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

}
break;
case 4:
if (!parts[2].isEmpty() && isNullOrEmpty(d.getType())) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This uses isNullOrEmpty(d.getType()) but the equivalent code in DefaultModelNormalizer (line 264) uses isNullOrEmptyOrDefault(d.getType()) — which also treats "jar" as empty/default. The normalizer version is more correct: if a dependency already has type="jar" set explicitly, the normalizer will still set the type from the id attribute, but this consumer POM builder code won't. Consider using isNullOrEmptyOrDefault here too for consistency, or better yet, extract the shared parsing logic.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Fixed in 4632bb8c13: both case-4 and case-5 now use isNullOrEmptyOrDefault(d.getType()), and the missing isNullOrEmptyOrDefault helper is added to the consumer POM builder.

@gnodet gnodet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review of the latest state (commits 153b6c49e3 through 1cd2408a68) — signal PUSHED.

All PR-specific unit tests pass locally:

  • DefaultModelNormalizerTest: 37/37 ✅
  • DefaultModelValidatorTest: 107/107 ✅
  • MavenStaxReaderTest: 14/14 ✅
  • ConsumerPomArtifactTransformerTest: 11/11 ✅
  • DependencyIdStrategyTest: 35/35 ✅

Previous review findings resolved:

  1. IT compilation error — Fixed: no-arg constructor ✅
  2. dependencyManagement deduplication — Fixed in 1cd2408a68: extracted expandDependencyIds() that only expands id attributes without deduplication. expandAndDeduplicateDependencies now only applies to model.getDependencies()

Confirmed findings (1 medium, 2 low):

1. Type check inconsistency between normalizer and consumer POM builder (medium)

The normalizer's expandDependencyId uses isNullOrEmptyOrDefault(d.getType()) (accepts null, "", or "jar") when deciding whether to set the type from the id attribute. The consumer POM builder's expandSingleDependencyId uses isNullOrEmpty(d.getType()) instead — it won't overwrite a default "jar" type.

This means a dependency with <type>jar</type> as a child element and id="g:a:war:1.0" would be expanded differently by the normalizer vs the consumer POM builder. In practice, the validator catches conflicts before the consumer POM builder runs, so this is more of a consistency issue than a bug. But the consumer POM builder copy should use isNullOrEmptyOrDefault to match the normalizer, since the two are meant to implement the same expansion logic.

DefaultModelNormalizer.java line 264:

if (!parts[2].isEmpty() && isNullOrEmptyOrDefault(d.getType())) {

DefaultConsumerPomBuilder.java line 937:

if (!parts[2].isEmpty() && isNullOrEmpty(d.getType())) {

2. ~150 lines of duplicated expansion logic (low)

The expandSingleDependencyId, expandDependencyIdList, expandExclusionIdList, and isNullOrEmpty methods are nearly identical copy-paste between DefaultModelNormalizer and DefaultConsumerPomBuilder. This is a maintenance risk — the type check inconsistency above is a direct consequence of this duplication. Consider extracting to a shared utility class, or having the consumer POM builder delegate to the normalizer.

3. Missing mixin validation tests (low)

Still unfixed from the previous review. ~48 lines of mixin id/gav validation logic (format check + child element conflict) in DefaultModelValidator have no corresponding test cases in DefaultModelValidatorTest. The normalizer and parser tests cover expansion/parsing, but the validator error paths for malformed mixin id attributes remain untested.

Findings dropped after verification:

  • Dedup key inconsistency in validator (noted in prior reviews, low impact, validator prevents the pathological case)
  • expandAndDeduplicateDependencies null return contract — the callers handle it correctly

Overall, the PR is in good shape. The dedup regression fix is clean and well-scoped. The main actionable item is the type check inconsistency (#1) which is a straightforward fix.

This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.

Claude Code on behalf of gnodet

The consumer POM builder's expandSingleDependencyId() used isNullOrEmpty()
for the type field in 4-part and 5-part id attributes, while the normalizer
uses isNullOrEmptyOrDefault() which also treats "jar" as empty/default.

This caused a behavioral divergence: the normalizer would set the type from
the id attribute even when a dependency had type="jar" explicitly, but the
consumer POM builder would not.

Harmonize by using isNullOrEmptyOrDefault() in both places.
@gnodet

gnodet commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Fix pushed in 4632bb8c13: harmonized type-check in consumer POM builder to use isNullOrEmptyOrDefault() matching the normalizer, as flagged in the review.

Also addresses the observation about duplicated isNullOrEmpty — the consumer POM builder now has its own isNullOrEmptyOrDefault() private helper, consistent with the normalizer's approach.

@gnodet
gnodet dismissed their stale review September 9, 2026 22:37

All findings from this review have been addressed in subsequent commits. IT compilation error fixed, dependencyManagement dedup regression fixed, type-check inconsistency fixed.

@gnodet gnodet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Re-review after commit 4632bb8c13 — signal PUSHED.

All PR-specific unit tests pass locally:

  • DefaultModelNormalizerTest + DefaultModelValidatorTest: ✅
  • ConsumerPomArtifactTransformerTest: ✅
  • DependencyIdStrategyTest: ✅
  • MavenStaxReaderTest: ✅

Previous finding resolved:

  1. Type check inconsistency between normalizer and consumer POM builderFixed in 4632bb8c13: both now use isNullOrEmptyOrDefault(d.getType()) for type fields in 4-part and 5-part ids. The expansion logic in DefaultConsumerPomBuilder.expandSingleDependencyId() and DefaultModelNormalizer.expandDependencyId() is now fully aligned. ✅

Remaining findings (2 low):

1. Missing mixin validation tests (low)

~48 lines of mixin id/gav validation logic in DefaultModelValidator (lines 378-424: format check requiring exactly 3 parts, and child element conflict checks for groupId, artifactId, version) have no corresponding test cases. The normalizer and StAX reader tests cover expansion/parsing, but the validator error paths for malformed mixin id attributes remain untested. Not a blocker, but a test coverage gap.

2. Duplicated expansion logic across normalizer and consumer POM builder (low)

~150 lines of id expansion logic (expandSingleDependencyId, expandDependencyIdList, expandExclusionIdList, isNullOrEmpty, isNullOrEmptyOrDefault) are near-verbatim duplicated between DefaultModelNormalizer and DefaultConsumerPomBuilder. The type-check inconsistency fixed in this commit was a direct consequence of this duplication. A shared utility class or static helper could prevent future drift. Not blocking for this PR, but worth a follow-up.

Branch status: 6 commits behind master, clean merge (no conflicts).

Overall the PR is in good shape — the main actionable finding from the previous review has been addressed. The remaining items are low-priority improvements.

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-bot gnodet-bot 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.

Re-review after commit 4632bb8c — the type-check inconsistency between normalizer and consumer POM builder is fixed.

Previous finding resolved:

  1. Type check inconsistency — Both case 4 and case 5 in DefaultConsumerPomBuilder.expandSingleDependencyId() now use isNullOrEmptyOrDefault(d.getType()), matching DefaultModelNormalizer.expandDependencyId(). The missing isNullOrEmptyOrDefault helper is added. ✅

Remaining (low, not blocking):

  • ~150 lines of duplicated expansion logic across normalizer and consumer POM builder — maintenance risk, but not blocking for this PR.
  • ~48 lines of mixin id/gav validation logic in DefaultModelValidator have no corresponding test cases — not blocking.

This review was generated by an AI agent, Hermès on behalf of @gnodet.

Each <dependency> and <exclusion> element in the validator test fixtures
now has a comment directly above it describing the expected outcome,
making it possible to understand the test intent without cross-referencing
DefaultModelValidatorTest.java.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request mvn4

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants