mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-08-29 03:06:45 +08:00
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:
parent
b5ef5a2d90
commit
9f001efa29
@ -27,7 +27,7 @@ bom {
|
||||
]
|
||||
}
|
||||
}
|
||||
library("Commons Compress", "1.20") {
|
||||
library("Commons Compress", "1.21") {
|
||||
group("org.apache.commons") {
|
||||
modules = [
|
||||
"commons-compress"
|
||||
|
@ -74,13 +74,12 @@ 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
|
||||
+ "' is not compatible with buildpacks; ensure jar file is valid and launch script is not enabled");
|
||||
private void assertArchiveHasEntries(File file) {
|
||||
try (ZipFile zipFile = new ZipFile(file)) {
|
||||
Assert.state(zipFile.getEntries().hasMoreElements(), () -> "Archive file '" + file + "' is not valid");
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException("File '" + jarFile + "' is not readable", ex);
|
||||
throw new IllegalStateException("File '" + file + "' is not readable", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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]]
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -20,12 +20,7 @@ import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import java.nio.file.attribute.PosixFilePermission;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipException;
|
||||
|
||||
@ -39,6 +34,7 @@ import org.apache.commons.compress.archivers.jar.JarArchiveOutputStream;
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
* @author Madhura Bhave
|
||||
* @author Scott Frederick
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class JarWriter extends AbstractJarWriter implements AutoCloseable {
|
||||
@ -80,28 +76,15 @@ public class JarWriter extends AbstractJarWriter implements AutoCloseable {
|
||||
*/
|
||||
public JarWriter(File file, LaunchScript launchScript, FileTime lastModifiedTime)
|
||||
throws FileNotFoundException, IOException {
|
||||
FileOutputStream fileOutputStream = new FileOutputStream(file);
|
||||
this.jarOutputStream = new JarArchiveOutputStream(new FileOutputStream(file));
|
||||
if (launchScript != null) {
|
||||
fileOutputStream.write(launchScript.toByteArray());
|
||||
setExecutableFilePermission(file);
|
||||
this.jarOutputStream.writePreamble(launchScript.toByteArray());
|
||||
file.setExecutable(true);
|
||||
}
|
||||
this.jarOutputStream = new JarArchiveOutputStream(fileOutputStream);
|
||||
this.jarOutputStream.setEncoding("UTF-8");
|
||||
this.lastModifiedTime = lastModifiedTime;
|
||||
}
|
||||
|
||||
private void setExecutableFilePermission(File file) {
|
||||
try {
|
||||
Path path = file.toPath();
|
||||
Set<PosixFilePermission> permissions = new HashSet<>(Files.getPosixFilePermissions(path));
|
||||
permissions.add(PosixFilePermission.OWNER_EXECUTE);
|
||||
Files.setPosixFilePermissions(path, permissions);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// Ignore and continue creating the jar
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeToArchive(ZipEntry entry, EntryWriter entryWriter) throws IOException {
|
||||
JarArchiveEntry jarEntry = asJarArchiveEntry(entry);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -49,6 +49,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
* @author Madhura Bhave
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
class RepackagerTests extends AbstractPackagerTests<Repackager> {
|
||||
|
||||
@ -159,6 +160,9 @@ class RepackagerTests extends AbstractPackagerTests<Repackager> {
|
||||
assertThat(new String(bytes)).startsWith("ABC");
|
||||
assertThat(hasLauncherClasses(source)).isFalse();
|
||||
assertThat(hasLauncherClasses(this.destination)).isTrue();
|
||||
try (ZipFile zipFile = new ZipFile(this.destination)) {
|
||||
assertThat(zipFile.getEntries().hasMoreElements()).isTrue();
|
||||
}
|
||||
try {
|
||||
assertThat(Files.getPosixFilePermissions(this.destination.toPath()))
|
||||
.contains(PosixFilePermission.OWNER_EXECUTE);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -106,7 +106,8 @@ abstract class AbstractLaunchScriptIntegrationTests {
|
||||
withCopyFileToContainer(
|
||||
MountableFile.forHostPath("src/intTest/resources/scripts/" + scriptsDir + testScript),
|
||||
"/" + testScript);
|
||||
withCommand("/bin/bash", "-c", "chmod +x " + testScript + " && ./" + testScript);
|
||||
withCommand("/bin/bash", "-c",
|
||||
"chown root:root *.sh && chown root:root *.jar && chmod +x " + testScript + " && ./" + testScript);
|
||||
withStartupCheckStrategy(new OneShotStartupCheckStrategy().withTimeout(Duration.ofMinutes(5)));
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user