Fail fast when attempting to repackage a reproducible war
Maven's war plugin does not support reproducible builds, resulting in the entries in the war file not being written in a consistent order from build to build. Closes gh-20176
This commit is contained in:
parent
ca202ad59f
commit
c4a55a5fb4
|
@ -21,6 +21,7 @@ import java.io.IOException;
|
|||
import java.nio.file.attribute.FileTime;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
import org.springframework.boot.loader.tools.Layouts.War;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
|
@ -100,6 +101,9 @@ public class Repackager extends Packager {
|
|||
public void repackage(File destination, Libraries libraries, LaunchScript launchScript, FileTime lastModifiedTime)
|
||||
throws IOException {
|
||||
Assert.isTrue(destination != null && !destination.isDirectory(), "Invalid destination");
|
||||
if (lastModifiedTime != null && getLayout() instanceof War) {
|
||||
throw new IllegalStateException("Reproducible repackaging is not supported with war packaging");
|
||||
}
|
||||
destination = destination.getAbsoluteFile();
|
||||
File source = getSource();
|
||||
if (isAlreadyPackaged() && source.equals(destination)) {
|
||||
|
|
|
@ -17,17 +17,12 @@
|
|||
package org.springframework.boot.maven;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.stream.Collectors;
|
||||
import java.io.FileReader;
|
||||
|
||||
import org.junit.jupiter.api.TestTemplate;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import org.springframework.boot.loader.tools.FileUtils;
|
||||
import org.springframework.util.FileSystemUtils;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
|
@ -72,34 +67,17 @@ class WarIntegrationTests extends AbstractArchiveIntegrationTests {
|
|||
}
|
||||
|
||||
@TestTemplate
|
||||
void whenWarIsRepackagedWithOutputTimestampConfiguredThenWarIsReproducible(MavenBuild mavenBuild)
|
||||
void whenWarIsRepackagedWithOutputTimestampTheBuildFailsAsItIsNotSupported(MavenBuild mavenBuild)
|
||||
throws InterruptedException {
|
||||
String firstHash = buildWarWithOutputTimestamp(mavenBuild);
|
||||
Thread.sleep(1500);
|
||||
String secondHash = buildWarWithOutputTimestamp(mavenBuild);
|
||||
assertThat(firstHash).isEqualTo(secondHash);
|
||||
mavenBuild.project("war-output-timestamp").executeAndFail((project) -> {
|
||||
try {
|
||||
String log = FileCopyUtils.copyToString(new FileReader(new File(project, "target/build.log")));
|
||||
assertThat(log).contains("Reproducible repackaging is not supported with war packaging");
|
||||
}
|
||||
|
||||
private String buildWarWithOutputTimestamp(MavenBuild mavenBuild) {
|
||||
AtomicReference<String> warHash = new AtomicReference<>();
|
||||
mavenBuild.project("war-output-timestamp").execute((project) -> {
|
||||
File repackaged = new File(project, "target/war-output-timestamp-0.0.1.BUILD-SNAPSHOT.war");
|
||||
assertThat(repackaged).isFile();
|
||||
assertThat(repackaged.lastModified()).isEqualTo(1584352800000L);
|
||||
try (JarFile jar = new JarFile(repackaged)) {
|
||||
List<String> unreproducibleEntries = jar.stream()
|
||||
.filter((entry) -> entry.getLastModifiedTime().toMillis() != 1584352800000L)
|
||||
.map((entry) -> entry.getName() + ": " + entry.getLastModifiedTime())
|
||||
.collect(Collectors.toList());
|
||||
assertThat(unreproducibleEntries).isEmpty();
|
||||
warHash.set(FileUtils.sha1Hash(repackaged));
|
||||
FileSystemUtils.deleteRecursively(project);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
});
|
||||
return warHash.get();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -146,7 +146,7 @@ public class RepackageMojo extends AbstractPackagerMojo {
|
|||
/**
|
||||
* Timestamp for reproducible output archive entries, either formatted as ISO 8601
|
||||
* (<code>yyyy-MM-dd'T'HH:mm:ssXXX</code>) or an {@code int} representing seconds
|
||||
* since the epoch.
|
||||
* since the epoch. Not supported with war packaging.
|
||||
* @since 2.3.0
|
||||
*/
|
||||
@Parameter(defaultValue = "${project.build.outputTimestamp}")
|
||||
|
|
Loading…
Reference in New Issue