gradle-versions-plugin
gradle-versions-plugin copied to clipboard
Checking dependency constraints not working
Although there is an option to enable checking the dependency constrains for updates, it doesn't work.
Example:
plugins {
id 'java'
id 'com.github.ben-manes.versions' version "0.39.0"
}
group = 'com.example'
sourceCompatibility = '11'
repositories { mavenCentral() }
dependencyUpdates.checkConstraints = true
dependencies {
implementation("ch.qos.logback:logback-classic:1.2.3")
constraints {
constraints {
implementation('org.slf4j:slf4j-api') { version { strictly '1.7.24' } }
}
}
}
Running the dependencyUpdates task results in the following output:
The following dependencies are using the latest milestone version:
- com.github.ben-manes.versions:com.github.ben-manes.versions.gradle.plugin:0.39.0
- org.slf4j:slf4j-api:1.7.24
The following dependencies have later milestone versions:
- ch.qos.logback:logback-classic [1.2.3 -> 1.3.0-alpha5]
http://logback.qos.ch
Checking on maven central shows, that the newest version of slf4j-api is in fact currently 2.0.0-alpha1.
Looking at the Resolver.groovy, around Line 154:
copy.dependencies.clear()
copy.dependencies.addAll(latest)
copy.dependencies.addAll(inherited)
This part cleans up the dependencies from the copied configuration prior running the resolver and doing the actual version checks.
Clearing the dependencyConstraints at that point makes it work like expected for me:
copy.dependencies.clear()
copy.dependencies.addAll(latest)
copy.dependencies.addAll(inherited)
copy.dependencyConstraints.clear()
I think the issue here might be the strictly
constraint, as it effectively disables any further resolution. As we need it to be strictly in our project to downgrade some transitive dependencies, we may not change it to another value, like require
or prefer
.
Could you please consider removing all constraints prior resolving the dependencies?
Thanks, Simon
@anuraaga contributed this feature and has some unit tests for it. I'd be happy to accept a PR with your changes and an additional test case. He'd be the best to review, as I haven't used constraints in my own projects.
Can you please explain how dependency constraints differ from a componentSelection / resolutionStrategy? From afar they appear similar and I don't understand why both exist.
As we perform a copy, we honor the resolutionStrategy as less surprising (also, I don't think the api allows us to remove it). That is sometimes desired and sometimes not. The workaround we suggest, e.g. in #361, is to conditionalize adding the resolutionStrategy by checking if the dependencyUpdates
task is going to be run. If so, then the rules like forcing versions can be skipped in the configuration. Perhaps there is a better way to do this, but no one has complained with that answer so far.
My naive impression is that constraints are similar and maybe we should recommend the same solution. I don't know if clearing it would be any more or less surprising, and a resolutionStrategy sets a precedence of honoring whatever the configuration is set as. If Gradle allowed for more manipulations, then we could have a callback to modify our copy to remove these rules to allow you to clear the constraints without us making that decision explicitly.
What do you guys think is the right approach here, @bratkartoffel, @anuraaga?
I agree that resolutionStrategy and strict dependencies seem similar so the current default of respecting the build could be least surprising. A code callback instead to customize the configuration could fallback well.
Sure, an option or callback to configure the behavior would be fine too. As this is beyond my groovy / gradle capabilities I cannot help with a PR in that case.
For the time being the below works without plugin changes. It is not pretty, but neither is the resolutionStrategy fix.
dependencyUpdates.checkConstraints = true
def strictVersion(DependencyConstraint constraint, String version) {
gradle.taskGraph.whenReady { taskGraph ->
constraint.version {
if (taskGraph.hasTask(dependencyUpdates)) {
require version
} else {
strictly version
}
}
}
}
dependencies {
implementation("ch.qos.logback:logback-classic:1.2.3")
constraints {
constraints {
implementation('org.slf4j:slf4j-api') {
strictVersion(it, '1.7.24')
}
}
}
}
For the plugin to make this all less hairy, I think a callback with the Configuration
would be the most powerful. That means DependencyUpdates.groovy would allow one to set an Action<? super Configuration>
and we'd dispatch to that in Resolver
on our copy
before resolving. Then a user could do any fix ups they think make sense, like clearing the dependencyConstraints
. This was done for resolutionStrategy
so you could use that as an example to follow.