Commit b545330d authored by Andy Wilkinson's avatar Andy Wilkinson

Fix reusable archive creation with Gradle 4.1 and later

Closes gh-11468
parent a6c301ed
...@@ -20,6 +20,8 @@ import java.io.File; ...@@ -20,6 +20,8 @@ import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
...@@ -39,7 +41,6 @@ import org.gradle.api.internal.file.copy.FileCopyDetailsInternal; ...@@ -39,7 +41,6 @@ import org.gradle.api.internal.file.copy.FileCopyDetailsInternal;
import org.gradle.api.specs.Spec; import org.gradle.api.specs.Spec;
import org.gradle.api.specs.Specs; import org.gradle.api.specs.Specs;
import org.gradle.api.tasks.WorkResult; import org.gradle.api.tasks.WorkResult;
import org.gradle.util.GUtil;
import org.springframework.boot.loader.tools.DefaultLaunchScript; import org.springframework.boot.loader.tools.DefaultLaunchScript;
import org.springframework.boot.loader.tools.FileUtils; import org.springframework.boot.loader.tools.FileUtils;
...@@ -52,6 +53,9 @@ import org.springframework.boot.loader.tools.FileUtils; ...@@ -52,6 +53,9 @@ import org.springframework.boot.loader.tools.FileUtils;
*/ */
class BootZipCopyAction implements CopyAction { class BootZipCopyAction implements CopyAction {
private static final long CONSTANT_TIME_FOR_ZIP_ENTRIES = new GregorianCalendar(1980,
Calendar.FEBRUARY, 1, 0, 0, 0).getTimeInMillis();
private final File output; private final File output;
private final boolean preserveFileTimestamps; private final boolean preserveFileTimestamps;
...@@ -158,20 +162,14 @@ class BootZipCopyAction implements CopyAction { ...@@ -158,20 +162,14 @@ class BootZipCopyAction implements CopyAction {
private void writeDirectory(ZipArchiveEntry entry, ZipArchiveOutputStream out) private void writeDirectory(ZipArchiveEntry entry, ZipArchiveOutputStream out)
throws IOException { throws IOException {
if (!this.preserveFileTimestamps) { prepareEntry(entry, UnixStat.DIR_FLAG | UnixStat.DEFAULT_DIR_PERM);
entry.setTime(GUtil.CONSTANT_TIME_FOR_ZIP_ENTRIES);
}
entry.setUnixMode(UnixStat.DIR_FLAG | UnixStat.DEFAULT_DIR_PERM);
out.putArchiveEntry(entry); out.putArchiveEntry(entry);
out.closeArchiveEntry(); out.closeArchiveEntry();
} }
private void writeClass(ZipArchiveEntry entry, ZipInputStream in, private void writeClass(ZipArchiveEntry entry, ZipInputStream in,
ZipArchiveOutputStream out) throws IOException { ZipArchiveOutputStream out) throws IOException {
if (!this.preserveFileTimestamps) { prepareEntry(entry, UnixStat.FILE_FLAG | UnixStat.DEFAULT_FILE_PERM);
entry.setTime(GUtil.CONSTANT_TIME_FOR_ZIP_ENTRIES);
}
entry.setUnixMode(UnixStat.FILE_FLAG | UnixStat.DEFAULT_FILE_PERM);
out.putArchiveEntry(entry); out.putArchiveEntry(entry);
byte[] buffer = new byte[4096]; byte[] buffer = new byte[4096];
int read; int read;
...@@ -181,6 +179,13 @@ class BootZipCopyAction implements CopyAction { ...@@ -181,6 +179,13 @@ class BootZipCopyAction implements CopyAction {
out.closeArchiveEntry(); out.closeArchiveEntry();
} }
private void prepareEntry(ZipArchiveEntry entry, int unixMode) {
if (!this.preserveFileTimestamps) {
entry.setTime(CONSTANT_TIME_FOR_ZIP_ENTRIES);
}
entry.setUnixMode(unixMode);
}
private void writeLaunchScriptIfNecessary(FileOutputStream fileStream) { private void writeLaunchScriptIfNecessary(FileOutputStream fileStream) {
try { try {
if (this.launchScript != null) { if (this.launchScript != null) {
...@@ -280,7 +285,7 @@ class BootZipCopyAction implements CopyAction { ...@@ -280,7 +285,7 @@ class BootZipCopyAction implements CopyAction {
private long getTime(FileCopyDetails details) { private long getTime(FileCopyDetails details) {
return this.preserveFileTimestamps ? details.getLastModified() return this.preserveFileTimestamps ? details.getLastModified()
: GUtil.CONSTANT_TIME_FOR_ZIP_ENTRIES; : CONSTANT_TIME_FOR_ZIP_ENTRIES;
} }
} }
......
...@@ -29,6 +29,7 @@ import org.junit.runner.RunWith; ...@@ -29,6 +29,7 @@ import org.junit.runner.RunWith;
import org.springframework.boot.gradle.junit.GradleCompatibilitySuite; import org.springframework.boot.gradle.junit.GradleCompatibilitySuite;
import org.springframework.boot.gradle.testkit.GradleBuild; import org.springframework.boot.gradle.testkit.GradleBuild;
import org.springframework.boot.loader.tools.FileUtils;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
...@@ -56,6 +57,21 @@ public abstract class AbstractBootArchiveIntegrationTests { ...@@ -56,6 +57,21 @@ public abstract class AbstractBootArchiveIntegrationTests {
.getOutcome()).isEqualTo(TaskOutcome.SUCCESS); .getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
} }
@Test
public void reproducibleArchive() throws InvalidRunnerConfigurationException,
UnexpectedBuildFailure, IOException, InterruptedException {
assertThat(this.gradleBuild.build(this.taskName).task(":" + this.taskName)
.getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
File jar = new File(this.gradleBuild.getProjectDir(), "build/libs")
.listFiles()[0];
String firstHash = FileUtils.sha1Hash(jar);
Thread.sleep(1500);
assertThat(this.gradleBuild.build("clean", this.taskName)
.task(":" + this.taskName).getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
String secondHash = FileUtils.sha1Hash(jar);
assertThat(firstHash).isEqualTo(secondHash);
}
@Test @Test
public void upToDateWhenBuiltTwice() throws InvalidRunnerConfigurationException, public void upToDateWhenBuiltTwice() throws InvalidRunnerConfigurationException,
UnexpectedBuildFailure, IOException { UnexpectedBuildFailure, IOException {
......
buildscript {
dependencies {
classpath files(pluginClasspath.split(','))
}
}
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
bootJar {
mainClassName = 'com.example.Application'
preserveFileTimestamps = false
reproducibleFileOrder = true
}
buildscript {
dependencies {
classpath files(pluginClasspath.split(','))
}
}
apply plugin: 'war'
apply plugin: 'org.springframework.boot'
bootWar {
mainClassName = 'com.example.Application'
preserveFileTimestamps = false
reproducibleFileOrder = true
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment