Skip to content

Maven: fold a renamed dependency into an existing declaration of another scope - #9061

Merged
jkschneider merged 1 commit into
mainfrom
duplicate-dependency-different-scope
Oct 6, 2026
Merged

jkschneider merged 1 commit into
mainfrom
duplicate-dependency-different-scope

Conversation

@jkschneider

Copy link
Copy Markdown
Member

ChangeDependencyGroupIdAndArtifactId renames a dependency declaration in place. Since #8129 it removes the renamed declaration instead when the pom already declares the new coordinates, but only if classifier, type and scope all match. When only the scope differs, the pom ends up with two declarations of the same groupId:artifactId:type:classifier. Maven 3 warns about that and lets the last declaration win, which can silently narrow a compile dependency to test scope. Maven 4 fails the build with "must be unique".

What changes:

  • A renamed declaration is folded into an existing declaration of the new coordinates whatever its scope, as long as classifier and type match.
  • The declaration that remains takes the wider of the two scopes: compile over any other, provided or runtime over test, and compile where one is provided and the other runtime, as those two only overlap on the test classpath.
  • A declaration in a scope that is not a classpath scope, such as system or import, is left as it was.

Recipes that rename onto coordinates a pom already declares in another scope now remove a declaration where they used to leave two, and may widen the scope of the one that remains.

Found by coding agents reviewing a run of org.openrewrite.java.testing.mockito.ReplacePowerMockito over six repositories. In konik32/openrest, pom.xml declares mockito-core in compile scope and powermock-api-mockito in test scope, and the migration turned the second into another mockito-core. victorrentea/design-patterns has the same shape. The agents wrote the problem up as duplicate-dependency-different-scope. They report that with this change the migration, built from source and run on each of the two repositories, leaves one mockito-core declaration.

ChangeDependencyGroupIdAndArtifactIdTest passes, 75 tests with one skipped, four of them added here: one for each scope combination above. They follow the fixtures of the existing deduplication tests in that class, not the poms of the two repositories.

Known limits:

  • Where the new coordinates are managed, the remaining declaration loses its explicit version, as it already did when the scopes matched. In design-patterns that moves mockito-core to the version Spring Boot manages.
  • <exclusions> and <optional> of the removed declaration are not carried over to the remaining one.
  • A declaration in an active profile and one in the main <dependencies> are folded together.
  • ChangeManagedDependencyGroupIdAndArtifactId is unchanged.

… into an existing declaration of another scope

Maven keys a dependency on groupId:artifactId:type:classifier, so a renamed
declaration that only differs in scope from an existing one is a duplicate:
Maven 3 warns and lets the last declaration win, Maven 4 fails the build.
`ChangeDependencyGroupIdAndArtifactId` now removes the old declaration in that
case too, and widens the scope of the surviving one to cover both.
@jkschneider
jkschneider merged commit 460e08c into main Oct 6, 2026
1 check passed
@jkschneider
jkschneider deleted the duplicate-dependency-different-scope branch October 6, 2026 05:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant