Skip to content

[#11767] Fix consumer POM builder failing to resolve BOM imports from…#11768

Closed
DavidTavoularis wants to merge 2 commits into
apache:masterfrom
DavidTavoularis:fix/consumer-pom-repositories
Closed

[#11767] Fix consumer POM builder failing to resolve BOM imports from…#11768
DavidTavoularis wants to merge 2 commits into
apache:masterfrom
DavidTavoularis:fix/consumer-pom-repositories

Conversation

@DavidTavoularis
Copy link
Copy Markdown
Contributor

… settings.xml profile repositories

DefaultConsumerPomBuilder.buildModel() now passes repositories, profiles, and active profile IDs to the ModelBuilderRequest so that BOM imports from non-central repositories (e.g. defined in settings.xml profiles) can be resolved during consumer POM transformation.

DefaultModelBuilder.derive() now respects the request's repositories instead of always reusing the parent session's, and caches the RepositoryFactory lookup.

Includes unit tests for both fixes and an integration test (gh-11767).

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.

…s from settings.xml profile repositories

DefaultConsumerPomBuilder.buildModel() now passes repositories, profiles, and
active profile IDs to the ModelBuilderRequest so that BOM imports from
non-central repositories (e.g. defined in settings.xml profiles) can be resolved
during consumer POM transformation.

DefaultModelBuilder.derive() now respects the request's repositories instead of
always reusing the parent session's, and caches the RepositoryFactory lookup.

Includes unit tests for both fixes and an integration test (apachegh-11767).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@gnodet gnodet left a comment

Choose a reason for hiding this comment

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

Review

The fix correctly addresses the root cause: DefaultConsumerPomBuilder.buildModel() wasn't passing repositories/profiles to the ModelBuilderRequest, and DefaultModelBuilder.derive() was ignoring request repositories. The integration test is solid. A few observations:

1. The derive() change is broad. The repository override is applied in the general derive(ModelBuilderRequest, DefaultModelBuilderResult) method, which affects all derived sessions — not just BUILD_CONSUMER. This could have unintended side effects on other request types. Consider scoping the override to only apply for BUILD_CONSUMER requests.

2. Lazy RepositoryFactory caching. The new getRepositoryFactory() with lazy init adds a mutable field. This could be simplified by calling session.getService(RepositoryFactory.class) directly (likely already cached in the session), avoiding the extra state.

3. The instanceof InternalMavenSession check is fragile. Profile/activeProfileIds injection only works when the session is an InternalMavenSession. In embedded or testing contexts, profiles silently won't be passed. Worth documenting or handling explicitly.

4. Unit test uses reflection. testDeriveSessionUsesRequestRepositories accesses mainSession, repositories, and externalRepositories via reflection, making it brittle to field renames. Consider adding package-private accessors or testing behavior indirectly (e.g., verify BOM resolution succeeds from a custom repo).

5. Integration test is well done. MavenITConsumerPomBomFromSettingsRepoTest with the local repo + settings.xml profile setup is the most valuable part — good coverage of the real-world scenario.

Claude Code on behalf of Guillaume Nodet

…MER, remove reflection

- Scope repository override in derive() to BUILD_CONSUMER requests only,
  preventing unintended side effects on parent POM resolution
- Remove repositoryFactory caching field, inline session.getService() calls
- Add LOGGER.debug for non-InternalMavenSession to document the limitation
- Replace reflection-based test with package-private accessors on
  ModelBuilderSessionState (getRepositories/getExternalRepositories)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@DavidTavoularis
Copy link
Copy Markdown
Contributor Author

All 4 review items addressed. Here's a summary of the changes:

  1. Scoped derive() repository override to BUILD_CONSUMER only (DefaultModelBuilder.java:351): Added request.getRequestType() == BUILD_CONSUMER check so the repository override doesn't affect parent POM resolution or other derived sessions.
  2. Removed repositoryFactory caching field (DefaultModelBuilder.java): Deleted the mutable repositoryFactory field and getRepositoryFactory() method. Now calls session.getService(RepositoryFactory.class) directly at both call sites.
  3. Added explicit handling for non-InternalMavenSession (DefaultConsumerPomBuilder.java:337-342): Added an else branch with a LOGGER.debug() message explaining that settings.xml profiles won't be passed in embedded/testing contexts.
  4. Replaced reflection-based test with package-private accessor test (DefaultModelBuilderTest.java): Rewrote testDeriveSessionUsesRequestRepositories → testBuildConsumerWithExplicitRepositories. Added getRepositories() and getExternalRepositories() accessors to ModelBuilderSessionState. The test now uses package-private access to mainSession and these accessors to directly assert both repositories and externalRepositories contain the custom repo — no reflection needed.

Copy link
Copy Markdown
Contributor

@gnodet gnodet left a comment

Choose a reason for hiding this comment

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

Re-review after feedback commit (4d5cd97)

The second commit cleanly addresses all 5 observations from the previous review:

  1. derive() scoped to BUILD_CONSUMER -- The repository override in derive() is now guarded by request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_CONSUMER, so parent POM resolution and other derived sessions are not affected. Good.

  2. RepositoryFactory caching removed -- The mutable repositoryFactory field and getRepositoryFactory() method are gone. The service is looked up inline via session.getService(RepositoryFactory.class) at both call sites. Simpler and correct.

  3. Non-InternalMavenSession handling -- The else branch now logs a LOGGER.debug() message explaining that settings.xml profiles won't be passed in embedded/testing contexts. This makes the limitation visible rather than silent.

  4. Reflection removed from unit test -- testBuildConsumerWithExplicitRepositories uses package-private accessors (getRepositories() / getExternalRepositories()) on ModelBuilderSessionState instead of reflection. The test is in the same package (org.apache.maven.impl.model), so this works cleanly and is resilient to refactoring.

  5. Integration test unchanged -- Still the most valuable part of the PR, verifying end-to-end BOM resolution from a settings.xml profile repository with consumer POM flattening.

The ConsumerPomBuilderTest addition using Mockito spy to capture and verify the ModelBuilderRequest is also well structured -- it confirms the request carries repositories without depending on internal state.

One minor note: no CI checks have run on this branch yet. Worth confirming CI passes before merge.

LGTM.

Claude Code on behalf of Guillaume Nodet

@gnodet gnodet added this to the 4.0.0-rc-6 milestone Jun 4, 2026
Copy link
Copy Markdown
Member

@hboutemy hboutemy left a comment

Choose a reason for hiding this comment

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

thanks

@gnodet gnodet closed this in 3656bff Jun 8, 2026
gnodet pushed a commit that referenced this pull request Jun 8, 2026
… settings.xml profile repositories

DefaultConsumerPomBuilder.buildModel() now passes repositories, profiles, and active profile IDs
to the ModelBuilderRequest so that BOM imports from non-central repositories (e.g. defined in
settings.xml profiles) can be resolved during consumer POM transformation.

DefaultModelBuilder.derive() now respects the request's repositories instead of always reusing
the parent session's, and caches the RepositoryFactory lookup.

Includes unit tests for both fixes and an integration test (gh-11767).

Contributed by DavidTavoularis

Closes #11768
Fixes #11767
@github-actions github-actions Bot removed this from the 4.0.0-rc-6 milestone Jun 8, 2026
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.

3 participants