Fix reusable archive creation with Gradle 4.1 and later

Closes gh-11468
pull/11460/merge
Andy Wilkinson 7 years ago
parent a6c301edb4
commit b545330d8e

@ -20,6 +20,8 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Function;
@ -39,7 +41,6 @@ import org.gradle.api.internal.file.copy.FileCopyDetailsInternal;
import org.gradle.api.specs.Spec;
import org.gradle.api.specs.Specs;
import org.gradle.api.tasks.WorkResult;
import org.gradle.util.GUtil;
import org.springframework.boot.loader.tools.DefaultLaunchScript;
import org.springframework.boot.loader.tools.FileUtils;
@ -52,6 +53,9 @@ import org.springframework.boot.loader.tools.FileUtils;
*/
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 boolean preserveFileTimestamps;
@ -158,20 +162,14 @@ class BootZipCopyAction implements CopyAction {
private void writeDirectory(ZipArchiveEntry entry, ZipArchiveOutputStream out)
throws IOException {
if (!this.preserveFileTimestamps) {
entry.setTime(GUtil.CONSTANT_TIME_FOR_ZIP_ENTRIES);
}
entry.setUnixMode(UnixStat.DIR_FLAG | UnixStat.DEFAULT_DIR_PERM);
prepareEntry(entry, UnixStat.DIR_FLAG | UnixStat.DEFAULT_DIR_PERM);
out.putArchiveEntry(entry);
out.closeArchiveEntry();
}
private void writeClass(ZipArchiveEntry entry, ZipInputStream in,
ZipArchiveOutputStream out) throws IOException {
if (!this.preserveFileTimestamps) {
entry.setTime(GUtil.CONSTANT_TIME_FOR_ZIP_ENTRIES);
}
entry.setUnixMode(UnixStat.FILE_FLAG | UnixStat.DEFAULT_FILE_PERM);
prepareEntry(entry, UnixStat.FILE_FLAG | UnixStat.DEFAULT_FILE_PERM);
out.putArchiveEntry(entry);
byte[] buffer = new byte[4096];
int read;
@ -181,6 +179,13 @@ class BootZipCopyAction implements CopyAction {
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) {
try {
if (this.launchScript != null) {
@ -280,7 +285,7 @@ class BootZipCopyAction implements CopyAction {
private long getTime(FileCopyDetails details) {
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;
import org.springframework.boot.gradle.junit.GradleCompatibilitySuite;
import org.springframework.boot.gradle.testkit.GradleBuild;
import org.springframework.boot.loader.tools.FileUtils;
import static org.assertj.core.api.Assertions.assertThat;
@ -56,6 +57,21 @@ public abstract class AbstractBootArchiveIntegrationTests {
.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
public void upToDateWhenBuiltTwice() throws InvalidRunnerConfigurationException,
UnexpectedBuildFailure, IOException {

@ -0,0 +1,14 @@
buildscript {
dependencies {
classpath files(pluginClasspath.split(','))
}
}
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
bootJar {
mainClassName = 'com.example.Application'
preserveFileTimestamps = false
reproducibleFileOrder = true
}

@ -0,0 +1,14 @@
buildscript {
dependencies {
classpath files(pluginClasspath.split(','))
}
}
apply plugin: 'war'
apply plugin: 'org.springframework.boot'
bootWar {
mainClassName = 'com.example.Application'
preserveFileTimestamps = false
reproducibleFileOrder = true
}
Loading…
Cancel
Save