Very similar to https://github.com/spring-projects/spring-boot/issues/8308

That's an old issue however and details such as the name of the task and how exactly it is configured in build.gradle has changed. So rather than re-opening that issue I figured its probably better just to treat it as a new issue.

To reproduce:

  • create a new gradle boot project using intializr wizard. Select starters web, 'actuator' and devtools (actuator is probably not needed). All other settings leave at default (so using boot version 2.3.1 at the time of this writing).
  • edit 'build.gradle' and add this:
bootJar {
    excludeDevtools = false 
}

Build a jar:

./gradlew build

Expectation:

  • the jar should contain devtools

Realtity:

  • the jar doesn't contain devtools.

To verify this we looked for devtools dependency in the jar like so:

$ jar tf build/libs/gradle-paddle-0.0.1-SNAPSHOT.jar  | grep dev

The bug also affects docker image build, which is were we discovered it, while trying to implement 'devtools integration' for STS with STS docker support.

This bug is a blocker for us as it means implementing devtools integration for gradle-based docker deployment of a boot project is not possible at the moment (which means we will only be (able to) supporting it for maven project for the time being).

Somewhat related to this. I wonder if it would be possible to allow controlling this option via a commandline parameter as well? E.g. most convenient for us would be that it obeys the same system property that the maven plugin obeys, so that basically we can make devtools be included into a build by adding -Dspring-boot.repackage.excludeDevtools=false on the build command for maven and gradle in the exact same way. Slightly less convenient would be another system property or command-line parameter, the most inconvenient would be that it requires the ide to make an actual change in the user's build.gradle file on disk. (I can raise this as a separate issue, just wanted to get a sense on whether it sounds reasonable to you first).

Comment From: wilkinsona

excludeDevtools is deprecated and is only applicable if you're not following the recommended way of adding Devtools to a Gradle project. If you follow the recommended approach using the developmentOnly configuration (as an app from start.spring.io does), then Devtools can be included in a fat jar or war by adding the developmentOnly configuration to the archive's classpath. In your case, using bootJar, that would look like the following:

bootJar {
    classpath configurations.developmentOnly
}

I wonder if it would be possible to allow controlling this option via a commandline parameter as well?

While it would be possible for us to add a property to automatically add the developmentOnly configuration to a fat jar or war, I'm not sure that it's warranted. If you control the command line that is used to launch Gradle (as I presume you do if a command line parameter is of interest), you can use an init script to customise the build to meet your needs. For example, that init script could automatically add the developmentOnly configuration to the classpath of every BootJar or BootWar task in the project.

Comment From: kdvolder

Thanks Andy. Adding this to build.gradle:

bootJar {
    classpath configurations.developmentOnly
}

... works nicely. But this requires we modify build.gradle file.

We tried then to do something similar via an init script. We've tried a few variants:

Variant 1:

bootJar {
    classpath configurations.developmentOnly
}

Variant 2:

initscript {
    bootJar {
        classpath configurations.developmentOnly
    }
}

Variant 3:

allprojects {
    bootJar {
        classpath configurations.developmentOnly
    }
}

None of these works and results each time with an error which seems to say that 'bootJar' is not a known method.

My 'gradle fu' is very limited and googling docs about gradle init scrips didn't really help much. If you know how to do this so it works, could you please share the details?

Thanks for the help.

Comment From: wilkinsona

The init script runs at a point when the bootJar task hasn't been defined. Rather than assuming that the bootJar task already exists, you need to react to tasks being created and configure them at that point. The following will configure all BootJar tasks to include Devtools:

allprojects {
    tasks.matching { 
        it.class.name == 'org.springframework.boot.gradle.tasks.bundling.BootJar_Decorated'
    }.all { bootJar ->
        bootJar.classpath configurations.developmentOnly
    }
}

Alternatively, if you only want to change the configuration of the default bootJar task (rather than also configuring any custom BootJar tasks that the user has defined themselves), you could use the following:

allprojects {
    tasks.matching { task ->
        task.name == 'bootJar'
    }.all { bootJar ->
        bootJar.classpath configurations.developmentOnly
    }
}

Comment From: kdvolder

Thanks Andy, eventually we figured out that this also seems to work:

    allprojects {
        afterEvaluate {
            bootJar {
                classpath configurations.developmentOnly
            }
        }
    }

Comment From: wilkinsona

👍 afterEvaluate defers things long enough for the bootJar task to have been created. It's roughly equivalent to my second suggestion.