Skip to content

DevelopmentOnly configuration not picking up devtools version info from Gradle Java Platform #21569

@asaikali

Description

@asaikali

Project to reproduce issue at https://github.com/adib-samples/boot-issue-21569.git

I upgraded a project from Spring Boot 2.2.6 to 2.3.0 and run into an issue with the developmentOnly that is created automatically by the boot plugin.

pre upgrade setup

  • Spring Boot 2.2.6
  • Gradle 6.4.1 with multiple projects
  • Using Gradle Java Platform Plugin instead of the spring boot dependency plugin

configuration of the the platform project

plugins {
    `java-platform`
}

javaPlatform {
    allowDependencies()
}

dependencies {
    api(enforcedPlatform("org.springframework.boot:spring-boot-dependencies:${Versions.SpringBootVersion}"))
    constraints {
        api("com.github.bbottema:emailaddress-rfc2822:2.1.4")
        api("com.icegreen:greenmail:1.5.11")
        api("nl.jqno.equalsverifier:equalsverifier:3.1.13")
        api("com.google.guava:guava:28.2-jre")
        api("io.rest-assured:rest-assured:4.3.0")
        api("org.awaitility:awaitility:4.0.2")
        api("org.threeten:threeten-extra:1.5.0")
        api("com.vladmihalcea:hibernate-types-52:2.9.7")
    }
}

Then in the gradle project with the main spring boot main class I have

plugins {
    `java-library-conventions`
    `spring-boot-conventions`
}

springBoot {
    mainClassName = "com.example.MyApplication"
}

dependencies {
    implementation(project(":common:core"))
    implementation(project(":common:email"))
    implementation(project(":common:security"))
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("org.springframework.boot:spring-boot-starter-validation")
    implementation("org.springframework.boot:spring-boot-starter-data-jpa")
    implementation("org.springframework.boot:spring-boot-starter-actuator")
    implementation("org.springframework.boot:spring-boot-starter-thymeleaf")
    developmentOnly("org.springframework.boot:spring-boot-devtools")

    testImplementation(testFixtures(project(":common:security")))
    testImplementation("org.flywaydb:flyway-core")
    testRuntimeOnly("org.postgresql:postgresql")
}

notice that developmentOnly("org.springframework.boot:spring-boot-devtools") does not specify the spring boot version it is being brought in via the java platform project applied using the java-library-conventions project pluginin frob buildSrc (reproduced at end of issue for reference). Before upgrading to Spring Boot 2.3.0 this used to work just fine, I had to define my own developmentOnly configuration in my java-library-conventions plugin in bulidSrc usind the kotlin-dsl

val developmentOnly by configurations.creating // used by spring boot devtools
configurations {
    runtimeClasspath {
        extendsFrom(developmentOnly)
    }
}

After upgrade to Boot 2.3.0 I got an error message that developmentOnly was being defined by the plugin so I removed my own definition. However now I get the error below. Which I can resolve by changing

developmentOnly("org.springframework.boot:spring-boot-devtools")

to

developmentOnly("org.springframework.boot:spring-bootdevtools:${Versions.SpringBootVersion}")

It seems that the developmentOnly configuration does not read the version constraints from the Java Platfrom defined in the project.

error message when the boot version is not explicitly set on devtools

./gradlew build --stacktrace
> Task :snapcoach:app:bootJar FAILED

During the build, one or more dependencies that were declared without a version failed to resolve:
    org.springframework.boot:spring-boot-devtools:

Did you forget to apply the io.spring.dependency-management plugin to the app project?


FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':snapcoach:app:bootJar'.
> Could not resolve all files for configuration ':snapcoach:app:developmentOnly'.
   > Could not find org.springframework.boot:spring-boot-devtools:.
     Required by:
         project :snapcoach:app

* Try:
Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Exception is:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':snapcoach:app:bootJar'.
        at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:38)
        at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:77)
        at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55)
        at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
        at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:416)
        at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:406)
        at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165)
        at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250)
        at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158)
        at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:102)
        at org.gradle.internal.operations.DelegatingBuildOperationExecutor.call(DelegatingBuildOperationExecutor.java:36)
        at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
        at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:41)
        at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:370)
        at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:357)
        at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:350)
        at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:336)
        at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.lambda$run$0(DefaultPlanExecutor.java:127)
        at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:191)
        at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.executeNextNode(DefaultPlanExecutor.java:182)
        at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:124)
        at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
        at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
        at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
Caused by: org.gradle.api.internal.artifacts.ivyservice.DefaultLenientConfiguration$ArtifactResolveException: Could not resolve all files for configuration ':snapcoach:app:developmentOnly'.
        at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration.rethrowFailure(DefaultConfiguration.java:1265)
        at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration.access$1800(DefaultConfiguration.java:141)
        at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration$ConfigurationFileCollection.visitContents(DefaultConfiguration.java:1242)
        at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration$ConfigurationFileCollection.getFiles(DefaultConfiguration.java:1229)
        at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration.getFiles(DefaultConfiguration.java:484)
        at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration_Decorated.getFiles(Unknown Source)
        at org.gradle.api.internal.file.SubtractingFileCollection.getFiles(SubtractingFileCollection.java:56)
        at org.gradle.api.internal.file.SubtractingFileCollection.getFiles(SubtractingFileCollection.java:57)
        at org.gradle.api.internal.file.AbstractFileCollection.iterator(AbstractFileCollection.java:111)
        at org.gradle.api.internal.file.FilteredFileCollection.iterator(FilteredFileCollection.java:69)
        at org.gradle.api.internal.file.collections.DefaultFileCollectionResolveContext$FileTreeConverter.convertInto(DefaultFileCollectionResolveContext.java:165)
        at org.gradle.api.internal.file.collections.DefaultFileCollectionResolveContext.doResolve(DefaultFileCollectionResolveContext.java:103)
        at org.gradle.api.internal.file.collections.DefaultFileCollectionResolveContext.resolveAsFileTrees(DefaultFileCollectionResolveContext.java:89)
        at org.gradle.api.internal.file.FileCollectionBackFileTree.visitContents(FileCollectionBackFileTree.java:41)
        at org.gradle.api.internal.file.FilteredFileTree.visitContents(FilteredFileTree.java:59)
        at org.gradle.api.internal.file.collections.DefaultFileCollectionResolveContext.add(DefaultFileCollectionResolveContext.java:61)
        at org.gradle.api.internal.file.collections.UnpackingVisitor.add(UnpackingVisitor.java:44)
        at org.gradle.api.internal.file.collections.UnpackingVisitor.add(UnpackingVisitor.java:76)
        at org.gradle.api.internal.file.DefaultFileCollectionFactory$ResolvingFileCollection.visitContents(DefaultFileCollectionFactory.java:306)
        at org.gradle.api.internal.file.collections.DefaultFileCollectionResolveContext.add(DefaultFileCollectionResolveContext.java:61)
        at org.gradle.api.internal.tasks.PropertyFileCollection.visitContents(PropertyFileCollection.java:47)
        at org.gradle.api.internal.file.collections.DefaultFileCollectionResolveContext.add(DefaultFileCollectionResolveContext.java:61)
        at org.gradle.api.internal.tasks.properties.DefaultTaskProperties$2.visitContents(DefaultTaskProperties.java:130)
        at org.gradle.api.internal.file.CompositeFileCollection.getSourceCollections(CompositeFileCollection.java:145)
        at org.gradle.api.internal.file.CompositeFileCollection.isEmpty(CompositeFileCollection.java:100)
        at org.gradle.api.internal.tasks.execution.DefaultEmptySourceTaskSkipper.skipIfEmptySources(DefaultEmptySourceTaskSkipper.java:66)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$TaskExecution.skipIfInputsEmpty(ExecuteActionsTaskExecuter.java:519)
        at org.gradle.internal.execution.steps.SkipEmptyWorkStep.execute(SkipEmptyWorkStep.java:47)
        at org.gradle.internal.execution.steps.SkipEmptyWorkStep.execute(SkipEmptyWorkStep.java:34)
        at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsStartedStep.execute(MarkSnapshottingInputsStartedStep.java:39)
        at org.gradle.internal.execution.steps.LoadExecutionStateStep.execute(LoadExecutionStateStep.java:40)
        at org.gradle.internal.execution.steps.LoadExecutionStateStep.execute(LoadExecutionStateStep.java:28)
        at org.gradle.internal.execution.impl.DefaultWorkExecutor.execute(DefaultWorkExecutor.java:33)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:194)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:186)
        at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:114)
        at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46)
        at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:62)
        at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:57)
        at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:56)
        at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
        at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:77)
        at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55)
        at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
        at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:416)
        at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:406)
        at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165)
        at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250)
        at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158)
        at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:102)
        at org.gradle.internal.operations.DelegatingBuildOperationExecutor.call(DelegatingBuildOperationExecutor.java:36)
        at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
        at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:41)
        at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:370)
        at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:357)
        at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:350)
        at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:336)
        at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.lambda$run$0(DefaultPlanExecutor.java:127)
        at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:191)
        at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.executeNextNode(DefaultPlanExecutor.java:182)
        at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:124)
        at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
        at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
        at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
Caused by: org.gradle.internal.resolve.ModuleVersionNotFoundException: Could not find org.springframework.boot:spring-boot-devtools:.
Required by:
    project :snapcoach:app

buildSrc/src/main/kotlin/java-library-conventions.gradle.kts used by all projects in the build to apply the standard configuration

plugins {
    `java-library`
    jacoco
    checkstyle
    id("com.diffplug.gradle.spotless")
    id("com.gorylenko.gradle-git-properties")
}

java {
    sourceCompatibility = JavaVersion.VERSION_11
    targetCompatibility = JavaVersion.VERSION_11
}

dependencies {
    implementation(platform(project(":platform")))
    implementation("org.springframework.boot:spring-boot-starter")
    api("com.google.guava:guava")
    testImplementation("nl.jqno.equalsverifier:equalsverifier")
    testImplementation("org.springframework.boot:spring-boot-starter-test") {
        exclude(group = "org.junit.vintage", module = "junit-vintage-engine")
    }
}

tasks.jar {
    // take the project path name and use it as the jar file name, for example a project
    // :shared:core wolud generate a jar file sharde-core-version.jar
    archiveBaseName.set(project.path.replace(":","-").substring(1))
}

tasks.test {
    useJUnitPlatform()
    finalizedBy("jacocoTestReport")
    testLogging {
        showStandardStreams = false
        events("failed")
        showCauses = true
        showStackTraces = true
        setExceptionFormat("full")
    }
}

tasks.jacocoTestReport {
    reports {
        xml.isEnabled = true
        xml.destination = file("${buildDir}/jacoco/test.xml")
        html.isEnabled = true
        html.destination = file("${buildDir}/jacoco/html")
    }
}

tasks.jacocoTestCoverageVerification {
    violationRules {
        rule {
            limit {
                minimum = "0.1".toBigDecimal()
            }
        }
    }
}

tasks.compileJava {
    options.compilerArgs.add(element = "-parameters");
}

checkstyle {
    toolVersion = "8.32"
    configDirectory.set(rootProject.file("buildSrc/src/main/resources/checkstyle"))
    isShowViolations = true
    maxErrors = 0
    maxWarnings = 0
    isIgnoreFailures = false
}

spotless {
    java {
        googleJavaFormat()
        @Suppress("INACCESSIBLE_TYPE")
        licenseHeaderFile("${rootDir}/buildSrc/src/main/resources/spotless/masteryway.license.java")
    }
    sql {
        target("**/*.sql")
        dbeaver().configFile("${rootDir}/buildSrc/src/main/resources/spotless/dbeaver.properties")
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    status: invalidAn issue that we don't feel is valid

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions