spring-gradle-plugins / dependency-management-plugin

A Gradle plugin that provides Maven-like dependency management functionality
686 stars 85 forks source link

Unexpected managed versions due to the use of the Gradle project's version property during bom resolution #315

Closed mjustin closed 2 years ago

mjustin commented 2 years ago

This is quite possibly all working as intended now that I understand what's happening, but I want to flag it as an issue just in case there's a better way to handle it.

I was attempting to use the Dependency Management Plugin to import the org.springdoc:springdoc-openapi BOM. However, I soon discovered that rather than using the version of springdoc-openapi I specified, it is instead using the version of my Gradle project.

version = '0.0.1'

dependencyManagement {
    imports {
        mavenBom 'org.springdoc:springdoc-openapi:1.6.1'
    }
}

dependencies {
    implementation 'org.springdoc:springdoc-openapi-ui'
}
$ ./gradlew dependencies --configuration runtimeClasspath

> Task :dependencies

------------------------------------------------------------
Root project 'springdoc-openapi-issue'
------------------------------------------------------------

runtimeClasspath - Runtime classpath of source set 'main'.
\--- org.springdoc:springdoc-openapi-ui -> 0.0.1 FAILED

A web-based, searchable dependency report is available by adding the --scan option.

BUILD SUCCESSFUL in 2s
1 actionable task: 1 executed

Digging into it, this appears to be because the project's BOM uses the standard Maven property version as the version of the springdoc-openapi-ui dependency.

<dependencyManagement>
  <dependencies>
    <!-- ... -->
    <dependency>
      <groupId>org.springdoc</groupId>
      <artifactId>springdoc-openapi-ui</artifactId>
      <version>${version}</version>
    </dependency>
    <!-- ... -->
  </dependencies>
</dependencyManagement>

Since version is also one of the standard Gradle project properties, and since the dependency management plugin uses (amongst other things) the project properties to override the version of a property, the standard Gradle version property is automagically used for the springdoc-openapi-ui version.

This can of course be overridden using bomProperty:

mavenBom('org.springdoc:springdoc-openapi:1.6.1') {
    bomProperty 'version', '1.6.1'
}

Obviously, this all violates the principal of least surprise for someone who hopes to import a Maven BOM. Furthermore, unless the imported BOM were in the same project group as the Gradle project importing it, there is virtually no situation where it would make sense to use the Gradle version property in this specific scenario.

However, from the point of view of the BOM author, the Maven version property makes sense to use here as it should match the value of the BOM project. The issue is that this implementation decision is leaking out and affecting how the Gradle dependency management plugin is importing the BOM. Note that it does not affect how the BOM is imported into a Maven project by default, so it makes sense that a build author may not be considering the Gradle ecosystem when creating a Maven BOM artifact.

What is the preferred solution here? Is this something that should be addressed within the Gradle dependency management plugin, e.g. don't consider standard Gradle project properties such as version when overriding BOM managed versions? Should BOMs like this be changed to use a more override-friendly property, e.g. springdoc-openapi.version, setting that property to equal version within the Maven BOM? Or does it make the most sense to just continue using bomProperty when importing the BOM into a Gradle project when using the dependency management plugin?

Related springdoc-openapi issue: https://github.com/springdoc/springdoc-openapi/issues/1401

wilkinsona commented 2 years ago

It looks like this has been addressed in Spring Doc 1.6.3. In my experience ${version} is atypical in a Maven pom with ${project.version} being much more common. The change made in Springdoc has moved in this direction.

With that said, I think that it's very unlikely that the Gradle project's version property is going to be a useful replacement for any ${version} placeholder in an imported bom so I think it should be ignored by ProjectPropertySource.