Adjust fat jar central directory to account for launch script

An upgrade to Apache Commons Compress allows the build plugins to write
the launch script to the fat jar as a proper preamble, making the file
compatible with more jar and zip tooling.

Fixes gh-22336
This commit is contained in:
Scott Frederick
2021-07-19 15:02:05 -05:00
parent b5ef5a2d90
commit 9f001efa29
11 changed files with 36 additions and 46 deletions

View File

@@ -8,9 +8,6 @@ See the {buildpacks-reference}/reference/spec/platform-api/#users[CNB specificat
The task is automatically created when the `java` or `war` plugin is applied and is an instance of {boot-build-image-javadoc}[`BootBuildImage`].
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.
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]]

View File

@@ -206,7 +206,7 @@ On Unix-like platforms, this launch script allows the archive to be run directly
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.
It is recommended that you only enable this option if you intend to execute it directly, rather than running it with `java -jar` or deploying it to a servlet container.
To use this feature, the inclusion of the launch script must be enabled:

View File

@@ -135,8 +135,8 @@ class BootZipCopyAction implements CopyAction {
}
private void writeArchive(CopyActionProcessingStream copyActions, OutputStream output) throws IOException {
writeLaunchScriptIfNecessary(output);
ZipArchiveOutputStream zipOutput = new ZipArchiveOutputStream(output);
writeLaunchScriptIfNecessary(zipOutput);
try {
setEncodingIfNecessary(zipOutput);
Processor processor = new Processor(zipOutput);
@@ -148,15 +148,14 @@ class BootZipCopyAction implements CopyAction {
}
}
private void writeLaunchScriptIfNecessary(OutputStream outputStream) {
private void writeLaunchScriptIfNecessary(ZipArchiveOutputStream outputStream) {
if (this.launchScript == null) {
return;
}
try {
File file = this.launchScript.getScript();
Map<String, String> properties = this.launchScript.getProperties();
outputStream.write(new DefaultLaunchScript(file, properties).toByteArray());
outputStream.flush();
outputStream.writePreamble(new DefaultLaunchScript(file, properties).toByteArray());
this.output.setExecutable(true);
}
catch (IOException ex) {

View File

@@ -279,11 +279,14 @@ abstract class AbstractBootArchiveTests<T extends Jar & BootArchive> {
properties.put("initInfoProvides", this.task.getArchiveBaseName().get());
properties.put("initInfoShortDescription", this.project.getDescription());
properties.put("initInfoDescription", this.project.getDescription());
assertThat(Files.readAllBytes(this.task.getArchiveFile().get().getAsFile().toPath()))
File archiveFile = this.task.getArchiveFile().get().getAsFile();
assertThat(Files.readAllBytes(archiveFile.toPath()))
.startsWith(new DefaultLaunchScript(null, properties).toByteArray());
try (ZipFile zipFile = new ZipFile(archiveFile)) {
assertThat(zipFile.getEntries().hasMoreElements()).isTrue();
}
try {
Set<PosixFilePermission> permissions = Files
.getPosixFilePermissions(this.task.getArchiveFile().get().getAsFile().toPath());
Set<PosixFilePermission> permissions = Files.getPosixFilePermissions(archiveFile.toPath());
assertThat(permissions).contains(PosixFilePermission.OWNER_EXECUTE);
}
catch (UnsupportedOperationException ex) {

View File

@@ -235,12 +235,16 @@ class BootBuildImageIntegrationTests {
}
@TestTemplate
void failsWithLaunchScript() throws IOException {
void buildsImageWithLaunchScript() throws IOException {
writeMainClass();
writeLongNameResource();
BuildResult result = this.gradleBuild.buildAndFail("bootBuildImage");
assertThat(result.task(":bootBuildImage").getOutcome()).isEqualTo(TaskOutcome.FAILED);
assertThat(result.getOutput()).contains("not compatible with buildpacks");
BuildResult result = this.gradleBuild.build("bootBuildImage", "--pullPolicy=IF_NOT_PRESENT");
String projectName = this.gradleBuild.getProjectDir().getName();
assertThat(result.task(":bootBuildImage").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
assertThat(result.getOutput()).contains("docker.io/library/" + projectName);
assertThat(result.getOutput()).contains("---> Test Info buildpack building");
assertThat(result.getOutput()).contains("---> Test Info buildpack done");
removeImage(projectName);
}
@TestTemplate