Search code examples
spring-bootgradlegradle-kotlin-dslmaven-bom

Use BOM from Gradle plugin management


We use a BOM to share our dependency management in MyCompany.

It is defined as a Maven POM. Here is a minimal example:

<?xml version="1.0" encoding="UTF-8"?>
<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.mycompany.common</groupId>
  <artifactId>common-java</artifactId>
  <version>1.0.0</version>
  <packaging>pom</packaging>

  <properties>
    <spring-boot.version>2.4.0</spring-boot.version>
  </properties>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-dependencies</artifactId>
        <version>${spring-boot.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

</project>

It is then used from Gradle projects.

Although this works for code dependencies, I'd like to find out a way to use it from plugin management.

For now, we define in settings.gradle.kts:

pluginManagement {

    val springBootVersion: String by settings

    plugins {
        id("org.springframework.boot") version(springBootVersion)
    }
}

springBootVersion being defined in the gradle.properties.

This is an issue to me because Spring version is defined both:

  • in the shared BOM;
  • in each project's plugin management.

How can I access to that BOM from Gradle's plugin management? And if I can't, what is a good don't-repeat-yourself practice to do so?


Solution

  • Your custom platform can provide an opinion about which version of the Spring Boot Gradle plugin you want clients to use (especially since it's not included in the spring-boot-dependencies BOM).

    Here are the relevant parts of an example platform's build.gradle.kts file, for example:

    plugins {
        `java-platform`
    }
    
    javaPlatform {
        allowDependencies()
    }
    
    dependencies {
    
        // This platform extends the Spring Boot platform.
        api(platform("org.springframework.boot:spring-boot-dependencies:2.7.6"))
    
        constraints {
            // Provide an opinion about which version of the Spring Boot Gradle plugin clients
            // should use since it's not included in the standard spring-boot-dependencies BOM.
            api("org.springframework.boot:spring-boot-gradle-plugin:2.7.6")
        }
    
    }
    

    That will generate a BOM that aligns both spring-boot-gradle-plugin and spring-boot-dependencies:

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-gradle-plugin</artifactId>
                <version>2.7.6</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.7.6</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    

    Your client projects can then just depend upon your platform and inherit its opinion about the Spring Boot version using something like this:

    buildSrc/build.gradle.kts:

    // Pull in the version of the Spring Boot Gradle plugin specified by your
    // platform, making it available to your regular build script.
    dependencies {
        implementation(enforcedPlatform("my-group:my-base-bom:1.0.0"))
        implementation("org.springframework.boot:spring-boot-gradle-plugin")
    }
    

    build.gradle.kts:

    plugins {
        id("org.springframework.boot") // version inherited from your platform
    }
    
    dependencies {
        // It's necessary to specify it for each configuration.
        implementation(enforcedPlatform("my-group:my-base-bom:1.0.0"))
    
        // Pull in any normal Spring Boot-managed dependencies you need (versions come from platform).
        implementation("org.springframework.boot:spring-boot-starter-web")
    }
    

    Of course, you could also use Gradle version catalogs to centralize versions, which are inlined for clarity in the examples.