Confusion with Gradle 4.6 Maven BOM import recommendations (constraints?)

I am working on a project and I am generating a Maven BOM that projects can reference for standard versions of dependencies. I was about to start implementing the nebula-dependency-recommender-plugin but I noticed it is now deprecated and the project now points to Gradle’s new BOM capabilities (since 4.6).

I started looking into it and and at first it seemed like exactly what I needed… I expected that if you don’t specify a version of your dependency, you will get the recommend version for the dependency based on an imported BOM. This is how I understand the nebula plugin to work, from basic testing. Maybe this assumption and the fact that the nebula plugin is deprecated is where my confusion is coming from.

Please correct me if I’m wrong but with the Gradle implementation, it seems like it is constraining all versions of a dependency (including transitive) to the version listed in the BOM. This is what I’m seeing but maybe I’m doing something wrong? The title in the bom import section of the managing transitive dependencies user guide seems to suggest they are recommendations but from everything else I’m reading, and from testing, it appears that the BOM import is forcing all versions to the one specified in the BOM (a dependency constraint).

If that is not whats expected or there is a way to get the import to only set a version when one doesn’t exist, please let me know! I’m stuck between this and the now deprecated Nebula plugin. I’d really like to use the built in functionality if I can get it to work beyond just forcing dependency constraints. Maybe its possible using the new module metadata format?

Another option I’m looking at is implementing a custom versioning scheme but like I said, I’d prefer to utilize the built in BOM import capability, if possible.

Gradle is implementing native support of the Maven BOM. The intention of the BOM in Maven is to constrain the dependencies to a set of “blessed” versions. Regardless of which version is declared in the project or through transitive dependencies, the goal is to only use the ones provided in the BOM. Gradle’s implementation aligns with this design.

The Netflix Nebula Dependency Recommender Plugin implemented a method for leaving off the version when declaring dependencies. It can use the versions provided by the BOM as a recommendation source, but it is not actually implementing the same functionality as native BOM support. Netflix has deprecated this plugin because much of the functionality can now be achieved without the plugin. This does not mean that your implementation will be exactly the same.

The primary difference here is that there aren’t as many resolution options. Specifically, direct dependencies with a version qualifier from the Nebula plugin isn’t an option as this would not conform with how the BOM is supposed to work. If you really don’t want a version newer than what’s specified in the BOM, you just need to be more explicit about it, like was shown as the first option in the forced dependencies section for the Nebula plugin.

edit: See @Danny_Thomas1’s response for correction details.

@jjustinic, thanks for the reply. Thats pretty much what I understood and verified what I was thinking.

To me it seems like the Nebula dependency recommender plugin still provides some key features that are desirable and which, in my opinion, provide a compelling reason to keep it around.

Anyway, thanks again!

I’m one of the Nebula maintainers - I can add some color. In fact, we sponsored the development of this feature so we could deprecate our recommendations plugin.

@jjustinic isn’t quite right - Gradle’s BOM support works slightly differently. It provides something between our two recommendation strategies (force transitives or conflict resolved), in that it acts like a minimum version constraint for any dependency in the dependencyManagement section of a BOM or an optional dependency.

That is, if you declare a version less than the BOM/optional version, you get that version, if the prevailing version is higher, the constraint does nothing. They don’t appear at all in your dependencies nor dependencyInsight unless a version less than the constraint would have been selected.

We use the conflict resolved recommendation strategy internally, so this technically changes the behavior, but in reality projects consuming a BOM are also getting at least those versions from the dependency/ies that the BOM accompanies.

I can’t remember off the top of my head, but I believe you could also force a lesser version with your own constraint:

https://docs.gradle.org/4.6/release-notes.html#dependency-constraints-for-transitive-dependencies

Thanks, @Danny_Thomas1. I don’t have any cases where there is a newer version available than what is in the BOM, so I definitely missed observing that scenario where the declared version is higher.