Avoid triggering full transitive resolution from GradleDependencyConfiguration.findResolvedDependency#7455
Conversation
…iguration.findResolvedDependency findResolvedDependency iterates direct dependencies of the configuration looking for a match. It was calling getDirectResolved(), which triggers a full transitive download if the configuration has been marked for re-resolution (e.g. after AddDependency / UpgradeDependencyVersion / ChangeDependency mutations). Switch to getDirectResolvedShallow(), which resolves only the direct dependencies and defers the transitive closure until a caller actually walks it. The method is called from ChangeDependency, RemoveRedundantDependencyVersions, RemoveRedundantSecurityResolutionRules, GradleConfigurationFilter, and several downstream recipe repositories. In a migration recipe chain that rewrites many dependencies per build.gradle, each call was paying the full transitive-resolution cost. Updates one test (GradleProjectTest#changeConstraint) that searched for a transitive dependency via findResolvedDependency; it now iterates getResolved() directly, since the method is now scoped to direct dependencies of the configuration.
Jenson3210
left a comment
There was a problem hiding this comment.
Note: ResolvedDependency.findDependency() recursively walks this.dependencies. With getDirectResolvedShallow(), those dependency lists are empty (transitive POMs were never downloaded), so findDependency() effectively degrades to matching on the direct dependency's own coordinates only — it won't walk into transitives and won't trigger any downloads.
This means callers like AddJupiterDependencies (rewrite-testing-frameworks) that search for junit-jupiter-api via findResolvedDependency("org.junit.jupiter", "junit-jupiter-api") will no longer find it if it's only present as a transitive (e.g., pulled in by spring-boot-starter-test → junit-jupiter). The recipe would then redundantly add junit-jupiter — harmless but slightly noisy.
Worth noting in the Javadoc that this is now a direct-only match, since the method name findResolvedDependency doesn't make that obvious and callers in other repos (rewrite-testing-frameworks, rewrite-spring, rewrite-migrate-java) may rely on transitive matching.
Jenson3210
left a comment
There was a problem hiding this comment.
See comment above — findDependency() still walks this.dependencies recursively, but with shallow resolution those lists are empty. This silently changes findResolvedDependency from "search direct + transitive" to "search direct only" without updating callers in other repos that rely on transitive matching (AddJupiterDependencies, MergeBootstrapYaml, AddJaxbRuntime, etc.).
|
Perhaps we can build a smarter loop that stops the moment it is found and does not resolve the later/deeper dependencies anymore? |
What
GradleDependencyConfiguration.findResolvedDependency(groupId, artifactId)iterates the configuration's direct dependencies looking for a match. It was callinggetDirectResolved(), which triggers a full transitive download if the configuration has been marked for re-resolution (e.g. afterAddDependency/UpgradeDependencyVersion/ChangeDependencymutations).Switch it to
getDirectResolvedShallow(), which resolves only the direct dependencies and defers the transitive closure until a caller actually walks it.Why
The method is called from
ChangeDependency,RemoveRedundantDependencyVersions,RemoveRedundantSecurityResolutionRules,GradleConfigurationFilter, and several downstream recipe repositories (AddJupiterDependenciesinrewrite-testing-frameworks,MergeBootstrapYamlWithApplicationYamlinrewrite-spring,AddJaxbRuntime/AddJaxwsRuntimeinrewrite-migrate-java, etc.).build.gradle, eachfindResolvedDependencycall was paying the full transitive-resolution cost viaLazyResolutionContext.resolve()— which re-downloads POMs from the configured repositories. Building on Sam'sgetDirectResolvedShallow()work, this collapses another common hot path onto the shallow resolution.How
findResolvedDependency(String, String)now iteratesgetDirectResolvedShallow().getResolved()(the flat resolved list) directly.GradleProjectTest#changeConstraint, which usedfindResolvedDependencyto look upjackson-databind(a transitive ofrewrite-core). It now streamsgetResolved()and filters.Tests
rewrite-gradle:testsuite passes (20m 5s).GradleProjectTest,ChangeDependencyTest,RemoveRedundantDependencyVersionsTest,RemoveRedundantSecurityResolutionRulesTest,UpgradeDependencyVersionTestpass.