Skip to content

Commit bad6400

Browse files
committed
Merge pull request #25571 from Jurrie
* gh-25571: Support Maven's reproducible build feature for war repackaging Closes gh-25571
2 parents 9180220 + 3c0e236 commit bad6400

File tree

3 files changed

+28
-13
lines changed

3 files changed

+28
-13
lines changed

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

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

24-
import org.springframework.boot.loader.tools.Layouts.War;
2524
import org.springframework.util.Assert;
2625

2726
/**
@@ -102,9 +101,6 @@ public void repackage(File destination, Libraries libraries, LaunchScript launch
102101
throws IOException {
103102
Assert.isTrue(destination != null && !destination.isDirectory(), "Invalid destination");
104103
Layout layout = getLayout(); // get layout early
105-
if (lastModifiedTime != null && layout instanceof War) {
106-
throw new IllegalStateException("Reproducible repackaging is not supported with war packaging");
107-
}
108104
destination = destination.getAbsoluteFile();
109105
File source = getSource();
110106
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: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,20 @@
1717
package org.springframework.boot.maven;
1818

1919
import java.io.File;
20-
import java.io.FileReader;
2120
import java.io.IOException;
2221
import java.util.ArrayList;
2322
import java.util.List;
2423
import java.util.Map;
24+
import java.util.concurrent.atomic.AtomicReference;
2525
import java.util.jar.JarFile;
26+
import java.util.stream.Collectors;
2627

2728
import org.junit.jupiter.api.TestTemplate;
2829
import org.junit.jupiter.api.extension.ExtendWith;
2930

31+
import org.springframework.boot.loader.tools.FileUtils;
3032
import org.springframework.boot.loader.tools.JarModeLibrary;
31-
import org.springframework.util.FileCopyUtils;
33+
import org.springframework.util.FileSystemUtils;
3234

3335
import static org.assertj.core.api.Assertions.assertThat;
3436

@@ -78,17 +80,34 @@ void whenRequiresUnpackConfigurationIsProvidedItIsReflectedInTheRepackagedWar(Ma
7880
}
7981

8082
@TestTemplate
81-
void whenWarIsRepackagedWithOutputTimestampTheBuildFailsAsItIsNotSupported(MavenBuild mavenBuild)
83+
void whenWarIsRepackagedWithOutputTimestampConfiguredThenWarIsReproducible(MavenBuild mavenBuild)
8284
throws InterruptedException {
83-
mavenBuild.project("war-output-timestamp").executeAndFail((project) -> {
84-
try {
85-
String log = FileCopyUtils.copyToString(new FileReader(new File(project, "target/build.log")));
86-
assertThat(log).contains("Reproducible repackaging is not supported with war packaging");
85+
String firstHash = buildWarWithOutputTimestamp(mavenBuild);
86+
Thread.sleep(1500);
87+
String secondHash = buildWarWithOutputTimestamp(mavenBuild);
88+
assertThat(firstHash).isEqualTo(secondHash);
89+
}
90+
91+
private String buildWarWithOutputTimestamp(MavenBuild mavenBuild) {
92+
AtomicReference<String> warHash = new AtomicReference<>();
93+
mavenBuild.project("war-output-timestamp").execute((project) -> {
94+
File repackaged = new File(project, "target/war-output-timestamp-0.0.1.BUILD-SNAPSHOT.war");
95+
assertThat(repackaged).isFile();
96+
assertThat(repackaged.lastModified()).isEqualTo(1584352800000L);
97+
try (JarFile jar = new JarFile(repackaged)) {
98+
List<String> unreproducibleEntries = jar.stream()
99+
.filter((entry) -> entry.getLastModifiedTime().toMillis() != 1584352800000L)
100+
.map((entry) -> entry.getName() + ": " + entry.getLastModifiedTime())
101+
.collect(Collectors.toList());
102+
assertThat(unreproducibleEntries).isEmpty();
103+
warHash.set(FileUtils.sha1Hash(repackaged));
104+
FileSystemUtils.deleteRecursively(project);
87105
}
88-
catch (Exception ex) {
106+
catch (IOException ex) {
89107
throw new RuntimeException(ex);
90108
}
91109
});
110+
return warHash.get();
92111
}
93112

94113
@TestTemplate

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
@@ -148,7 +148,7 @@ public class RepackageMojo extends AbstractPackagerMojo {
148148
/**
149149
* Timestamp for reproducible output archive entries, either formatted as ISO 8601
150150
* (<code>yyyy-MM-dd'T'HH:mm:ssXXX</code>) or an {@code int} representing seconds
151-
* since the epoch. Not supported with war packaging.
151+
* since the epoch.
152152
* @since 2.3.0
153153
*/
154154
@Parameter(defaultValue = "${project.build.outputTimestamp}")

0 commit comments

Comments
 (0)