I have just updated to spring-boot-gradle-plugin-1.3.0 and realized the dependency management plugin is now included transitively.
I have a multi project build with a gradle resolution strategy applied to all subprojects. spring-boot is only used in one of those subprojects and the dependency plugin will silently override any resolution strategies for packages it has in its own dependency configuration
I might have missed it, but I could not figure out a way to entirely disable it.
I've done a little experimentation and I'm not sure that this is really necessary. If you configure your own resolution strategy after you've applied the Spring Boot plugin:
buildscript {
repositories {
mavenCentral()
maven { url 'https://repo.spring.io/libs-snapshot' }
}
dependencies {
classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.3.0.BUILD-SNAPSHOT'
}
}
apply plugin: 'spring-boot'
configurations.compile {
resolutionStrategy.eachDependency { DependencyResolveDetails details ->
if (details.requested.group == 'org.springframework') {
details.useVersion '4.0.0.RELEASE'
}
}
}
repositories {
mavenCentral()
maven { url 'https://repo.spring.io/libs-snapshot' }
}
dependencies {
compile 'org.springframework:spring-core'
}
It will take precedence over Boot's dependency management and will give you 4.0.0.RELEASE
of spring-core
:
gradle dependencies --configuration compile
:dependencies
------------------------------------------------------------
Root project
------------------------------------------------------------
compile - Compile classpath for source set 'main'.
\--- org.springframework:spring-core: -> 4.0.0.RELEASE
BUILD SUCCESSFUL
Total time: 6.983 secs
To have Spring Boot's dependency management take precedence you need to configure your own resolution strategy before you apply the Spring Boot plugin:
buildscript {
repositories {
mavenCentral()
maven { url 'https://repo.spring.io/libs-snapshot' }
}
dependencies {
classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.3.0.BUILD-SNAPSHOT'
}
}
apply plugin: 'java'
configurations.compile {
resolutionStrategy.eachDependency { DependencyResolveDetails details ->
if (details.requested.group == 'org.springframework') {
details.useVersion '4.0.0.RELEASE'
}
}
}
apply plugin: 'spring-boot'
repositories {
mavenCentral()
maven { url 'https://repo.spring.io/libs-snapshot' }
}
dependencies {
compile 'org.springframework:spring-core'
}
This will give you 4.2.0.RC1
of spring-core
:
gradle dependencies --configuration compile
:dependencies
------------------------------------------------------------
Root project
------------------------------------------------------------
compile - Compile classpath for source set 'main'.
\--- org.springframework:spring-core: -> 4.2.0.RC1
BUILD SUCCESSFUL
Total time: 6.188 secs
Furthermore, disabling the dependency management plugin entirely is unlikely to be what people really want. With the plugin disabled a number of Spring Boot's starters will pull in Commons Logging due to Gradle handling exclusions in a Maven pom differently to how Maven handles them. An alternative would be to allow the default bom to be configured. For example you could disable it entirely with:
springBoot {
dependencyManagementBom = null
}
However, I'm not sure that's even necessary because, as shown above, you can override Boot's dependency management without adding this new configuration option.
To summarise, I'm leaning towards documenting that any resolution strategy configured after the Spring Boot plugin has been applied will run after the dependency management plugin's resolution strategy giving you an opportunity to override its behaviour.
okay, but in a multi project build resolutions may have been defined before the dependency management plugin to avoid duplication of resolution strategies, eg.
subprojects {
configurations.all {
resolutionStrategy {
failOnVersionConflict()
...(more resolutions)
}
another thought would be to have the dependency management plugin at least recognize (it was quite irritating to have runtime breakage without notice just by applying the plugin) or even support resolution strategies defined at a parent level.
AFAIK, you don't _have_ to structure your build like that. Prior to that subprojects
block you could have applied the dependency management plugin to the one project that uses it. If I'm mistaken, please let me know.
There's no such thing as a "parent level" in Gradle as, unlike Maven, there's no inheritance hierarchy between projects. If you're interested, there was some discussion about this on the Gradle forum.
there's certainly a hierarchy thus also a parent level, even if there was no inheritance.
https://docs.gradle.org/current/userguide/organizing_build_logic.html
Inherited properties and methods. In a multi-project build, sub-projects inherit the properties and methods of their parent project.
yes, I could workaround this issue, but is it really a good solution? currently my root project doesn't contain any subproject specifics and I would prefer to keep it that (clean) way.
btw I have been using ext[..] version overrides as a workaround for now. the main issue is that conflict detection is effectively disabled, so I tried your suggestion of applying the plugin selectively prior to subprojects
.
the result is that dependency substitution (eg. resolutionStrategy.eachDependency
) works, but failOnVersionConflict
and force
overrides remain non-functional, so it's essentially no improvement over plain ext overrides.
Can you provide a sample that shows failOnVersionConflict
and force
not working please? The dependency management plugin doesn't make use of either of those so I'm surprised it has any effect.
sure, but first to clarify: failOnVersionConflict does not work for any dependency in the bom. Example: I have a child project (a) which requires activemq-5.11.+. Without the dep plugin, the spring-boot project which depends on (a) would raise a version conflict when claiming a different version of activemq. When using the dep plugin it will not, and I'm ending up with inconsistent deps without further notice. Do you still require a sample?
Do you still require a sample?
Yes please
Thanks.
The mix of versions for the ActiveMQ dependencies is happening because there's dependency management for some but not all of the dependencies. We should consider addressing that by providing dependency management in Spring Boot for all of ActiveMQ's modules rather than just the current handful. That's a separate issue, though.
I'm surprised to see that the version conflict detection doesn't work. It would appear that one closure that's applied to the configuration's resolution strategy is overriding the configuration from another and is somehow turning off the conflict detection. I need to do some more digging to understand why that's happening.
Scratch that. Gradle's reporting a conflict between different versions of the _same_ module. Gradle doesn't understand the concept of ActiveMQ being comprised of a number of modules that should all have the same version so it doesn't consider org.apache.activemq:activemq-broker:5.10.2
and org.apache.activemq:activemq-kahadb-store:5.11.1
as being in conflict.
You can imagine my surprise when I applied the plugin and the whole build went down.
Gradle is only detecting conflicts between same group and module, that's expected and sufficient I suppose as conflict resolution is applied to transitive dependencies as well, taking care of the rest.
In testing the spring-boot 1.3.0.RC1 build I ran into this problem as well. Within my spring-boot enabled project I am seeing module versions being altered, however this should fail because I have specified failOnVersionConflict()
. Here is a snippet from running gradle's dependencies task, at the moment I have about a dozen modules that suffer from this problem:
+--- com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.6.3 -> 2.6.1
+--- com.fasterxml.jackson.datatype:jackson-datatype-hppc:2.6.3
+--- com.fasterxml.jackson.datatype:jackson-datatype-joda:2.6.3 -> 2.6.1
+--- com.fasterxml.jackson.datatype:jackson-datatype-json-org:2.6.3
In addition to it not failing as I would expect, the dependency management plugin is silently downgrading select modules that are transitively pulled in via project dependencies. At the moment I'm left to either use the exact same versions as spring-boot, or risk compatibility issues with closely aligned modules like in the example above. Is there any other workaround?
I ended up dropping the whole spring-boot
plugin, as the only real use I had for it was bootRepackage
, which I no longer need.
@cmuchinsky Yes. Override the value of the jackson.version
property as described in the documentation
I can reproduce that version conflicts are no longer being reported with failOnVersionConflict() after upgrading to 1.3.x as well. I think this is a major regression. Any news?
I can reproduce that version conflicts are no longer being reported
It's not so much that they're not being reported but that, thanks to the dependency management, the conflicts are no longer there. Perhaps you have a use case where the conflicts are useful? I've yet to hear of one.
If you have a situation where Spring Boot isn't providing dependency management for all of a project's modules (as was the case with ActiveMQ that is discussed above) then we should address that.
@torstenwerner Can you provide some details about your situation please?
not only not providing dependency management, but also effectively disabling conflict detection in this case. modules of a project and bom could easily get "out of sync" as new modules are added to a group. isn't there a better/more reliable way of handling this?
not only not providing dependency management, but also effectively disabling conflict detection in this case
Can you explain why knowing there's a conflict, rather than the dependency management fixing it for you, is useful. I don't feel like I fully understand the problem which makes it hard to identify the best solution.
modules of a project and bom could easily get "out of sync" as new modules are added to a group. isn't there a better/more reliable way of handling this?
Based on my current understanding, I'd handle this by using the same dependency management across all projects in the build, either by using Spring Boot's plugin or the dependency management plugin directly.
Based on my current understanding, I'd handle this by using the same dependency management across all projects in the build, either by using Spring Boot's plugin or the dependency management plugin directly.
Fully agree, but the core problem was rather that Spring Boot's plugin did not report the conflict and let it pass silently, whereas grade would have reported it. I didn't check against the latest version, but if this has been improved now, this issue would actually be resolved.
on second thought that would imply you can't include sub-projects which do not rely on Spring Boot's plugin/dependency management reliably. That's a pretty strong restriction imho. So I suppose an off-switch for Spring Boot's dependency management is still the best solution if it can't be implemented in a way that it plays along well with gradle's conflict resolution in all cases.
My sample is https://github.com/torstenwerner/sample3164 . The conflict on 'org.jetbrains:annotations' is not managed by spring-boot. It is correctly reported as a conflict with version 1.2.8.RELEASE but not with 1.3.1.RELEASE.
Thanks for the sample, @torstenwerner. You're experiencing a different problem.
When you declare a version on a dependency, the dependency management plugin creates implicit dependency management for that dependency. This allows a dependency that declares a version to override any dependency management that may exist for that dependency. There's an option to turn off that behaviour:
dependencyManagement {
overriddenByDependencies = false
}
I've just noticed that I neglected to document it when I added it. It's now documented in the README.
With this overriding disabled, your sample projects reports a conflict for the org.jetbrains:annotations
dependency, rather than using 15.0.
It works now. Thanks!
We've recently updated to new spring boot plugin which had brought dependency management 'feature' included by default.
We use Wiremock server (https://github.com/tomakehurst/wiremock/) which relies on jetty 9.2.13.v20150730 and gradle is suppose to download it transitively. Unfortunately new spring boot plugin decided that new version of jetty should be used (9.3.14.v20161028) - wiremock started to fail because of newer jetty version. We don't explicitly define jetty version in our build.gradle and we actually don't want to, because it's a transitive dependency and should be managed by the lib. None of the other libs use jetty. Which is more important, we can see that version has changed in ./gradle dependency output (9.2.13.v20150730 -> 9.3.14.v20161028), but i cannot see where it comes from! Only enabling --debug i found that it's spring boot plugin.
So the question:
How can we disable dependency management plugin completely?
@abarysiuk This issue is closed and we prefer to keep the issue tracker for bugs and enhancement requests only. If you've got a question the best place to ask is stackoverflow.com.
I don't see it's closed @philwebb
I still do believe it's a good thing to have an option for disabling the plugin's dependency management instead of enforcing it on users who don't want or need it.
@masc3d Sorry about, you're right the issue is still open.
It occurred to me recently that this is actually already possible. You can add the Spring Boot plugin to your buildscript dependencies and then, rather than applying it, configure whatever tasks and dependency management you want to use. That's somewhat analogous to using Boot without auto-configuration. Configure whatever tasks you want is currently a little harder than it should be, but we hope to address that in 2.0 as we rework the plugin. We should keep this one open for now until we know exactly what that looks like in 2.0 in case you want to make further improvements.
+1 for a switch to disable the dependency management plugin.
Maybe there is room for improvement in configuration of different aspects, but I guess when using Gradle its built-in features should always work unless I override them explicitly. The way Spring Boot silently applies another dependency management as expected by the average Gradle user is quite surprising imho.
I personally want to manage dependencies by myself and I don't need the Maven-like approach. I only apply the Spring Boot plugin to change the packaging/publishing of my app, and I don't want the plugin to influence my compile or runtime dependencies.
In response to @wilkinsona's comment,
[... use] Spring Boot's plugin or the dependency management plugin directly.
An issue is that we can't use both. The Spring Boot plugin thwarts the usage of the dependency management plugin used in the "root" in the allprojects
block because it does some dependency management silently that can't be disabled. (All projects need dependency management to ensure they use the same version of artifacts, but only some projects need the Spring Boot plugin.)
Also, could you please explain this to a Gradle novice?
You can add the Spring Boot plugin to your buildscript dependencies and then, rather than applying it, configure whatever tasks and dependency management you want to use.
Do you mean roll-your-own repackage
task or is there a way to "import" the task without apply
ing the plugin?
Are there any plans to backport this fix to 1.5.x? It doesn't look like it relies on anything 2.x specific. See for example here: https://github.com/spring-projects/spring-boot/pull/10766
No, this change won鈥檛 be back-ported. I鈥檝e explained why on the PR.
Most helpful comment
I don't see it's closed @philwebb
I still do believe it's a good thing to have an option for disabling the plugin's dependency management instead of enforcing it on users who don't want or need it.