Skip to content

Commit c4a55a5

Browse files
committed
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
1 parent ca202ad commit c4a55a5

File tree

3 files changed

+13
-31
lines changed

3 files changed

+13
-31
lines changed

spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Repackager.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.nio.file.attribute.FileTime;
2222
import java.util.jar.JarFile;
2323

24+
import org.springframework.boot.loader.tools.Layouts.War;
2425
import org.springframework.util.Assert;
2526

2627
/**
@@ -100,6 +101,9 @@ public void repackage(File destination, Libraries libraries, LaunchScript launch
100101
public void repackage(File destination, Libraries libraries, LaunchScript launchScript, FileTime lastModifiedTime)
101102
throws IOException {
102103
Assert.isTrue(destination != null && !destination.isDirectory(), "Invalid destination");
104+
if (lastModifiedTime != null && getLayout() instanceof War) {
105+
throw new IllegalStateException("Reproducible repackaging is not supported with war packaging");
106+
}
103107
destination = destination.getAbsoluteFile();
104108
File source = getSource();
105109
if (isAlreadyPackaged() && source.equals(destination)) {

spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/WarIntegrationTests.java

Lines changed: 8 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,12 @@
1717
package org.springframework.boot.maven;
1818

1919
import java.io.File;
20-
import java.io.IOException;
21-
import java.util.List;
22-
import java.util.concurrent.atomic.AtomicReference;
23-
import java.util.jar.JarFile;
24-
import java.util.stream.Collectors;
20+
import java.io.FileReader;
2521

2622
import org.junit.jupiter.api.TestTemplate;
2723
import org.junit.jupiter.api.extension.ExtendWith;
2824

29-
import org.springframework.boot.loader.tools.FileUtils;
30-
import org.springframework.util.FileSystemUtils;
25+
import org.springframework.util.FileCopyUtils;
3126

3227
import static org.assertj.core.api.Assertions.assertThat;
3328

@@ -72,34 +67,17 @@ void whenRequiresUnpackConfigurationIsProvidedItIsReflectedInTheRepackagedWar(Ma
7267
}
7368

7469
@TestTemplate
75-
void whenWarIsRepackagedWithOutputTimestampConfiguredThenWarIsReproducible(MavenBuild mavenBuild)
70+
void whenWarIsRepackagedWithOutputTimestampTheBuildFailsAsItIsNotSupported(MavenBuild mavenBuild)
7671
throws InterruptedException {
77-
String firstHash = buildWarWithOutputTimestamp(mavenBuild);
78-
Thread.sleep(1500);
79-
String secondHash = buildWarWithOutputTimestamp(mavenBuild);
80-
assertThat(firstHash).isEqualTo(secondHash);
81-
}
82-
83-
private String buildWarWithOutputTimestamp(MavenBuild mavenBuild) {
84-
AtomicReference<String> warHash = new AtomicReference<>();
85-
mavenBuild.project("war-output-timestamp").execute((project) -> {
86-
File repackaged = new File(project, "target/war-output-timestamp-0.0.1.BUILD-SNAPSHOT.war");
87-
assertThat(repackaged).isFile();
88-
assertThat(repackaged.lastModified()).isEqualTo(1584352800000L);
89-
try (JarFile jar = new JarFile(repackaged)) {
90-
List<String> unreproducibleEntries = jar.stream()
91-
.filter((entry) -> entry.getLastModifiedTime().toMillis() != 1584352800000L)
92-
.map((entry) -> entry.getName() + ": " + entry.getLastModifiedTime())
93-
.collect(Collectors.toList());
94-
assertThat(unreproducibleEntries).isEmpty();
95-
warHash.set(FileUtils.sha1Hash(repackaged));
96-
FileSystemUtils.deleteRecursively(project);
72+
mavenBuild.project("war-output-timestamp").executeAndFail((project) -> {
73+
try {
74+
String log = FileCopyUtils.copyToString(new FileReader(new File(project, "target/build.log")));
75+
assertThat(log).contains("Reproducible repackaging is not supported with war packaging");
9776
}
98-
catch (IOException ex) {
77+
catch (Exception ex) {
9978
throw new RuntimeException(ex);
10079
}
10180
});
102-
return warHash.get();
10381
}
10482

10583
}

spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/RepackageMojo.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ public class RepackageMojo extends AbstractPackagerMojo {
146146
/**
147147
* Timestamp for reproducible output archive entries, either formatted as ISO 8601
148148
* (<code>yyyy-MM-dd'T'HH:mm:ssXXX</code>) or an {@code int} representing seconds
149-
* since the epoch.
149+
* since the epoch. Not supported with war packaging.
150150
* @since 2.3.0
151151
*/
152152
@Parameter(defaultValue = "${project.build.outputTimestamp}")

0 commit comments

Comments
 (0)