Skip to content

Commit 0e5df22

Browse files
Fail with Gradle bootBuildImage and war packaging
Prior to this commit, running the bootBuildImage Gradle task on a project configured for war packaging would result in a jar file being built and used in the image instead of the war file. With this commit an error will be thrown from the plugin in this case. Fixes gh-24521
1 parent 2930053 commit 0e5df22

File tree

6 files changed

+61
-1
lines changed

6 files changed

+61
-1
lines changed

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/asciidoc/packaging-oci-image.adoc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
[[build-image]]
22
== Packaging OCI Images
3-
The plugin can create an https://github.com/opencontainers/image-spec[OCI image] from executable jars using https://buildpacks.io[Cloud Native Buildpacks] (CNB).
3+
The plugin can create an https://github.com/opencontainers/image-spec[OCI image] from an executable jar file using https://buildpacks.io[Cloud Native Buildpacks] (CNB).
44
Images can be built using the `bootBuildImage` task.
55

66
NOTE: For security reasons, images build and run as non-root users.
77
See the {buildpacks-reference}/reference/spec/platform-api/#users[CNB specification] for more details.
88

99
The task is automatically created when the `java` plugin is applied and is an instance of {boot-build-image-javadoc}[`BootBuildImage`].
1010

11+
NOTE: The `bootBuildImage` task is not supported with projects using <<packaging-executable-wars, war packaging>>.
12+
1113
NOTE: The `bootBuildImage` task can not be used with a <<packaging-executable-configuring-launch-script, fully executable Spring Boot archive>> that includes a launch script.
1214
Disable launch script configuration in the `bootJar` task when building a jar file that is intended to be used with `bootBuildImage`.
1315

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/WarPluginAction.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,13 @@
2222
import org.gradle.api.artifacts.Configuration;
2323
import org.gradle.api.artifacts.ConfigurationContainer;
2424
import org.gradle.api.file.FileCollection;
25+
import org.gradle.api.file.RegularFile;
2526
import org.gradle.api.internal.artifacts.dsl.LazyPublishArtifact;
2627
import org.gradle.api.plugins.BasePlugin;
2728
import org.gradle.api.plugins.WarPlugin;
2829
import org.gradle.api.tasks.TaskProvider;
2930

31+
import org.springframework.boot.gradle.tasks.bundling.BootBuildImage;
3032
import org.springframework.boot.gradle.tasks.bundling.BootWar;
3133

3234
/**
@@ -51,6 +53,7 @@ public Class<? extends Plugin<? extends Project>> getPluginClass() {
5153
@Override
5254
public void execute(Project project) {
5355
disableWarTask(project);
56+
disableBootBuildImageTask(project);
5457
TaskProvider<BootWar> bootWar = configureBootWarTask(project);
5558
configureArtifactPublication(bootWar);
5659
}
@@ -59,6 +62,11 @@ private void disableWarTask(Project project) {
5962
project.getTasks().named(WarPlugin.WAR_TASK_NAME).configure((war) -> war.setEnabled(false));
6063
}
6164

65+
private void disableBootBuildImageTask(Project project) {
66+
project.getTasks().named(SpringBootPlugin.BOOT_BUILD_IMAGE_TASK_NAME, BootBuildImage.class)
67+
.configure((buildImage) -> buildImage.getJar().set((RegularFile) null));
68+
}
69+
6270
private TaskProvider<BootWar> configureBootWarTask(Project project) {
6371
return project.getTasks().register(SpringBootPlugin.BOOT_WAR_TASK_NAME, BootWar.class, (bootWar) -> {
6472
bootWar.setGroup(BasePlugin.BUILD_GROUP);

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootBuildImage.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.Map;
2222

2323
import org.gradle.api.DefaultTask;
24+
import org.gradle.api.GradleException;
2425
import org.gradle.api.JavaVersion;
2526
import org.gradle.api.Project;
2627
import org.gradle.api.Task;
@@ -79,6 +80,7 @@ public BootBuildImage() {
7980
* @return the jar property
8081
*/
8182
@Input
83+
@Optional
8284
public RegularFileProperty getJar() {
8385
return this.jar;
8486
}
@@ -226,6 +228,9 @@ public void setVerboseLogging(boolean verboseLogging) {
226228

227229
@TaskAction
228230
void buildImage() throws DockerEngineException, IOException {
231+
if (!this.jar.isPresent()) {
232+
throw new GradleException("Executable jar file required for building image");
233+
}
229234
Builder builder = new Builder();
230235
BuildRequest request = createRequest();
231236
builder.build(request);

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,36 @@ void failsWithInvalidImageName() {
153153
.containsPattern("example/Invalid-Image-Name");
154154
}
155155

156+
@TestTemplate
157+
void failsWithWarPackaging() {
158+
writeMainClass();
159+
writeLongNameResource();
160+
BuildResult result = this.gradleBuild.buildAndFail("bootBuildImage", "-PapplyWarPlugin");
161+
assertThat(result.task(":bootBuildImage").getOutcome()).isEqualTo(TaskOutcome.FAILED);
162+
assertThat(result.getOutput()).contains("Executable jar file required for building image");
163+
}
164+
165+
@TestTemplate
166+
void buildsImageWithWarPackagingAndJarConfiguration() throws IOException {
167+
writeMainClass();
168+
writeLongNameResource();
169+
BuildResult result = this.gradleBuild.build("bootBuildImage");
170+
String projectName = this.gradleBuild.getProjectDir().getName();
171+
assertThat(result.task(":bootBuildImage").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
172+
assertThat(result.getOutput()).contains("docker.io/library/" + projectName);
173+
assertThat(result.getOutput()).contains("paketo-buildpacks/builder");
174+
File buildLibs = new File(this.gradleBuild.getProjectDir(), "build/libs");
175+
assertThat(buildLibs.listFiles())
176+
.containsExactly(new File(buildLibs, this.gradleBuild.getProjectDir().getName() + ".war"));
177+
ImageReference imageReference = ImageReference.of(ImageName.of(projectName));
178+
try (GenericContainer<?> container = new GenericContainer<>(imageReference.toString())) {
179+
container.waitingFor(Wait.forLogMessage("Launched\\n", 1)).start();
180+
}
181+
finally {
182+
new DockerApi().image().remove(imageReference, false);
183+
}
184+
}
185+
156186
private void writeMainClass() {
157187
File examplePackage = new File(this.gradleBuild.getProjectDir(), "src/main/java/example");
158188
examplePackage.mkdirs();
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
plugins {
2+
id 'war'
3+
id 'org.springframework.boot' version '{version}'
4+
}
5+
6+
bootBuildImage {
7+
jar = bootWar.archiveFile
8+
}
9+
10+
sourceCompatibility = '1.8'
11+
targetCompatibility = '1.8'

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests.gradle

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,9 @@ plugins {
33
id 'org.springframework.boot' version '{version}'
44
}
55

6+
if (project.hasProperty('applyWarPlugin')) {
7+
apply plugin: 'war'
8+
}
9+
610
sourceCompatibility = '1.8'
711
targetCompatibility = '1.8'

0 commit comments

Comments
 (0)