Search code examples
spring-bootkotlingradlejib

Multi-project gradle build with jib cannot find dependencies for docker build


I want to try something new with kotlin and spring-boot in a gradle multi-project build. Multiple projects shall be combined in a jib generated docker image. But the first subproject is already causing issues, because the dependencies cannot be found.

My current setup is like this:

project
├── subproject1
|   ├── src
|   |   └── ...
│   └── build.gradle.kts
├── build.gradle.kts
└── settings.gradle.kts

project/settings.gradle.kts

rootProject.name = "project"
include("subproject1")

project/build.gradle.kts

plugins {
    kotlin("jvm") version "1.8.0"
    id("com.google.cloud.tools.jib") version "3.3.1"
}

jib {
    from {
        image = "eclipse-temurin:17-jre-alpine"
    }
    container {
        user = "myuser:myuser"
        ports = mutableListOf("8080")
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation(project(":subproject1"))
}

project/subproject1/build.gradle.kts

// mostly taken from start.spring.io
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
    kotlin("jvm")
    kotlin("plugin.spring") version "1.8.0"
    id("org.springframework.boot") version "3.0.5"
    id("io.spring.dependency-management") version "1.1.0"
}

group = "com.example"
version = "1.0"
java.sourceCompatibility = JavaVersion.VERSION_17

configurations {
    compileOnly {
        extendsFrom(configurations.annotationProcessor.get())
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-data-r2dbc")
    implementation("org.springframework.boot:spring-boot-starter-graphql")
    implementation("org.springframework.boot:spring-boot-starter-security")
    implementation("org.springframework.boot:spring-boot-starter-webflux")
    implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
    implementation("io.projectreactor.kotlin:reactor-kotlin-extensions")
    implementation("org.flywaydb:flyway-core")
    implementation("org.flywaydb:flyway-mysql")
    implementation("org.jetbrains.kotlin:kotlin-reflect")
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor")
    implementation("org.springframework:spring-jdbc")
    implementation("org.apache.commons:commons-collections4:4.4")
    implementation("org.apache.commons:commons-lang3:3.12.0")
    runtimeOnly("org.mariadb:r2dbc-mariadb:1.1.3")
    runtimeOnly("org.mariadb.jdbc:mariadb-java-client")
    testImplementation("org.springframework.boot:spring-boot-starter-test")
    testImplementation("io.projectreactor:reactor-test")
    testImplementation("org.springframework.graphql:spring-graphql-test")
    testImplementation("org.springframework.security:spring-security-test")
}

tasks.withType<KotlinCompile> {
    kotlinOptions {
        freeCompilerArgs = listOf("-Xjsr305=strict")
        jvmTarget = "17"
    }
}

tasks.withType<Test> {
    useJUnitPlatform()
}

I can run the build successfully:

$ ./gradlew bootJar --console=plain
> Task :subproject1:processResources
> Task :subproject1:compileKotlin
> Task :subproject1:compileJava NO-SOURCE
> Task :subproject1:classes
> Task :subproject1:jar
> Task :subproject1:inspectClassesForKotlinIC
> Task :subproject1:resolveMainClassName
> Task :subproject1:bootJar

BUILD SUCCESSFUL in 2s
6 actionable tasks: 6 executed

But trying to create a docker image fails:

$ ./gradlew jibDockerBuild --console=plain
> Task :subproject1:compileKotlin UP-TO-DATE
> Task :subproject1:compileJava NO-SOURCE
> Task :subproject1:processResources UP-TO-DATE
> Task :subproject1:classes UP-TO-DATE
> Task :subproject1:jar UP-TO-DATE
> Task :subproject1:inspectClassesForKotlinIC UP-TO-DATE
> Task :compileKotlin NO-SOURCE
> Task :compileJava NO-SOURCE
> Task :processResources NO-SOURCE
> Task :classes UP-TO-DATE
> Task :jar
> Task :inspectClassesForKotlinIC

> Task :jibDockerBuild FAILED
Tagging image with generated image reference project:latest. If you'd like to specify a different tag, you can set the jib.to.image parameter in your build.gradle, or use the --image=<MY IMAGE> commandline flag.

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':jibDockerBuild'.
> Could not resolve all dependencies for configuration ':runtimeClasspath'.
   > Could not find org.springframework.boot:spring-boot-starter-data-r2dbc:.
     Required by:
         project : > project :subproject1
   > Could not find org.springframework.boot:spring-boot-starter-graphql:.
     Required by:
         project : > project :subproject1
   > Could not find org.springframework.boot:spring-boot-starter-security:.
     Required by:
         project : > project :subproject1
   > Could not find org.springframework.boot:spring-boot-starter-webflux:.
     Required by:
         project : > project :subproject1
   > Could not find com.fasterxml.jackson.module:jackson-module-kotlin:.
     Required by:
         project : > project :subproject1
   > Could not find io.projectreactor.kotlin:reactor-kotlin-extensions:.
     Required by:
         project : > project :subproject1
   > Could not find org.flywaydb:flyway-core:.
     Required by:
         project : > project :subproject1
   > Could not find org.flywaydb:flyway-mysql:.
     Required by:
         project : > project :subproject1
   > Could not find org.jetbrains.kotlinx:kotlinx-coroutines-reactor:.
     Required by:
         project : > project :subproject1
   > Could not find org.springframework:spring-jdbc:.
     Required by:
         project : > project :subproject1
   > Could not find org.mariadb.jdbc:mariadb-java-client:.
     Required by:
         project : > project :subproject1

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 1s
7 actionable tasks: 3 executed, 4 up-to-date

When running the command with --stacktrace, I can see that gradle throws ModuleVersionNotFoundExceptions.

What is my mistake? What does it mean when gradle says that it cannot resolve those dependencies?


Solution

  • The solution is simple and surprising at the same time.

    As it turns out the subproject can be simply built using the latest versions of those dependencies, but jib in the main project cannot work without version specifications. I got it to work by adding each of the required dependency versions explicitly (which is probably a good idea anyway):

    dependencies {
        implementation("org.springframework.boot:spring-boot-starter-data-r2dbc:3.0.5")
        implementation("org.springframework.boot:spring-boot-starter-graphql:3.0.5")
        implementation("org.springframework.boot:spring-boot-starter-security:3.0.5")
        implementation("org.springframework.boot:spring-boot-starter-webflux:3.0.5")
        implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.14.2")
        implementation("io.projectreactor.kotlin:reactor-kotlin-extensions:1.2.2")
        // ... and so on
    }
    

    It's strange to me that the Spring Initializr is providing dependencies that work until you start using them in such a scenario. I still don't know if this is a jib or gradle problem (or intentional behavior).