diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/io/ZipFileTarArchive.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/io/ZipFileTarArchive.java index c6aea6c24c5..0555eb979a4 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/io/ZipFileTarArchive.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/io/ZipFileTarArchive.java @@ -35,6 +35,7 @@ import org.springframework.util.StreamUtils; * Adapter class to convert a ZIP file to a {@link TarArchive}. * * @author Phillip Webb + * @author Scott Frederick * @since 2.3.0 */ public class ZipFileTarArchive implements TarArchive { @@ -54,6 +55,7 @@ public class ZipFileTarArchive implements TarArchive { public ZipFileTarArchive(File zip, Owner owner) { Assert.notNull(zip, "Zip must not be null"); Assert.notNull(owner, "Owner must not be null"); + assertArchiveHasEntries(zip); this.zip = zip; this.owner = owner; } @@ -72,6 +74,16 @@ public class ZipFileTarArchive implements TarArchive { tar.finish(); } + private void assertArchiveHasEntries(File jarFile) { + try (ZipFile zipFile = new ZipFile(jarFile)) { + Assert.state(zipFile.getEntries().hasMoreElements(), "File '" + jarFile.toString() + + "' is not compatible with buildpacks; ensure jar file is valid and launch script is not enabled"); + } + catch (IOException ex) { + throw new IllegalStateException("File is not readable", ex); + } + } + private void copy(ZipArchiveEntry zipEntry, InputStream zip, TarArchiveOutputStream tar) throws IOException { TarArchiveEntry tarEntry = convert(zipEntry); tar.putArchiveEntry(tarEntry); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/asciidoc/packaging-oci-image.adoc b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/asciidoc/packaging-oci-image.adoc index c791d6466c6..7a5dfb60167 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/asciidoc/packaging-oci-image.adoc +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/asciidoc/packaging-oci-image.adoc @@ -4,7 +4,8 @@ The plugin can create an https://github.com/opencontainers/image-spec[OCI image] Images can be built using the `bootBuildImage` task. The task is automatically created when the `java` plugin is applied and is an instance of {boot-build-image-javadoc}[`BootBuildImage`]. - +NOTE: The `bootBuildImage` task can not be used with a <> that includes a launch script. +Disable launch script configuration in the `bootJar` task when building a jar file that is intended to be used with `bootBuildImage`. [[build-image-docker-daemon]] === Docker Daemon diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/asciidoc/packaging.adoc b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/asciidoc/packaging.adoc index 3c8d0f6a21f..ba7842db8b8 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/asciidoc/packaging.adoc +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/asciidoc/packaging.adoc @@ -201,6 +201,10 @@ Spring Boot provides support for fully executable archives. An archive is made fully executable by prepending a shell script that knows how to launch the application. On Unix-like platforms, this launch script allows the archive to be run directly like any other executable or to be installed as a service. +NOTE: Currently, some tools do not accept this format so you may not always be able to use this technique. +For example, `jar -xf` may silently fail to extract a jar or war that has been made fully-executable. +It is recommended that you only enable this option if you intend to execute it directly, rather than running it with `java -jar`, deploying it to a servlet container, or including it in an OCI image. + To use this feature, the inclusion of the launch script must be enabled: [source,groovy,indent=0,subs="verbatim,attributes",role="primary"] diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests.java index e3486bab96d..b24beebaa44 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests.java @@ -126,6 +126,15 @@ class BootBuildImageIntegrationTests { } } + @TestTemplate + void failsWithLaunchScript() { + writeMainClass(); + writeLongNameResource(); + BuildResult result = this.gradleBuild.buildAndFail("bootBuildImage"); + assertThat(result.task(":bootBuildImage").getOutcome()).isEqualTo(TaskOutcome.FAILED); + assertThat(result.getOutput()).contains("not compatible with buildpacks"); + } + @TestTemplate void failsWithBuilderError() { writeMainClass(); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-failsWithLaunchScript.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-failsWithLaunchScript.gradle new file mode 100644 index 00000000000..a719311038d --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-failsWithLaunchScript.gradle @@ -0,0 +1,11 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '{version}' +} + +sourceCompatibility = '1.8' +targetCompatibility = '1.8' + +bootJar { + launchScript() +} \ No newline at end of file