I am trying to build reproducible Docker images using spring-boot-jarmode-tools. However, the extracted application JAR file is not created reproducibly because the META-INF/MANIFEST.MF file has the current time as its last modification time. This affects the reproducibility of the Docker image as well. I have set a timestamp in Maven, so all the other files in the application JAR file have the correct last modification time. Ideally, I would like spring-boot-jarmode-tools to respect the environment variable SOURCE_DATE_EPOCH when extracting the application JAR file.
Steps to Reproduce
- Set the
SOURCE_DATE_EPOCH environment variable.
- Extract the same uber JAR file twice, at different times, to different locations, using
spring-boot-jarmode-tools.
- Compare the timestamps of the extracted files using a tool like
zipinfo.
- Observe that the timestamps in the
META-INF/MANIFEST.MF file vary.
This is the example output from a simple application:
Zip file size: 3705 bytes, number of entries: 12
-rw---- 2.0 fat 930 bX defN 26-Jan-02 23:50 META-INF/MANIFEST.MF
-rw---- 2.0 fat 0 bX defN 23-Jan-01 00:00 META-INF/services/
-rw---- 2.0 fat 0 bX defN 23-Jan-01 00:00 example/
-rw---- 2.0 fat 0 bX defN 23-Jan-01 00:00 example/org/
-rw---- 2.0 fat 0 bX defN 23-Jan-01 00:00 example/org/springframework/
-rw---- 2.0 fat 0 bX defN 23-Jan-01 00:00 example/org/springframework/boot/
-rw---- 2.0 fat 0 bX defN 23-Jan-01 00:00 META-INF/maven/
-rw---- 2.0 fat 0 bX defN 23-Jan-01 00:00 META-INF/maven/example.org.springframework.boot/
-rw---- 2.0 fat 0 bX defN 23-Jan-01 00:00 META-INF/maven/example.org.springframework.boot/spring-boot-example/
-rw---- 2.0 fat 804 bX defN 23-Jan-01 00:00 example/org/springframework/boot/SpringBootExampleApplication.class
-rw---- 2.0 fat 1071 bX defN 23-Jan-01 00:00 META-INF/maven/example.org.springframework.boot/spring-boot-example/pom.xml
-rw---- 2.0 fat 86 bX defN 23-Jan-01 00:00 META-INF/maven/example.org.springframework.boot/spring-boot-example/pom.properties
All files except META-INF/MANIFEST.MF have a fixed timestamp that I defined in Maven (2023-01-01T00:00:00Z).
This was tested with Spring Boot 4.0.1.
Expected Behavior
The JAR file should be created reproducibly, meaning all file modification times should be fixed and not based on the current time.
Actual Behavior
The META-INF/MANIFEST.MF file in the resulting JAR has the current time as its last modification time, making the JAR file non-reproducible.
Relevant Code
The issue is in the class org.springframework.boot.jarmode.tools.ExtractCommand. The class uses a standard JarOutputStream for the application JAR file, which writes the MANIFEST.MF file in its constructor and does not set an explicit timestamp.
Proposed Solution
I have a working solution that contains a new subclass of JarOutputStream which overrides JarOutputStream#putNextEntry to set a fixed timestamp (the value of SOURCE_DATE_EPOCH). I'd be happy to submit that, or any other solution, as a PR.
I am trying to build reproducible Docker images using
spring-boot-jarmode-tools. However, the extracted application JAR file is not created reproducibly because theMETA-INF/MANIFEST.MFfile has the current time as its last modification time. This affects the reproducibility of the Docker image as well. I have set a timestamp in Maven, so all the other files in the application JAR file have the correct last modification time. Ideally, I would likespring-boot-jarmode-toolsto respect the environment variable SOURCE_DATE_EPOCH when extracting the application JAR file.Steps to Reproduce
SOURCE_DATE_EPOCHenvironment variable.spring-boot-jarmode-tools.zipinfo.META-INF/MANIFEST.MFfile vary.This is the example output from a simple application:
All files except
META-INF/MANIFEST.MFhave a fixed timestamp that I defined in Maven (2023-01-01T00:00:00Z).This was tested with Spring Boot 4.0.1.
Expected Behavior
The JAR file should be created reproducibly, meaning all file modification times should be fixed and not based on the current time.
Actual Behavior
The
META-INF/MANIFEST.MFfile in the resulting JAR has the current time as its last modification time, making the JAR file non-reproducible.Relevant Code
The issue is in the class org.springframework.boot.jarmode.tools.ExtractCommand. The class uses a standard
JarOutputStreamfor the application JAR file, which writes theMANIFEST.MFfile in its constructor and does not set an explicit timestamp.Proposed Solution
I have a working solution that contains a new subclass of
JarOutputStreamwhich overrides JarOutputStream#putNextEntry to set a fixed timestamp (the value ofSOURCE_DATE_EPOCH). I'd be happy to submit that, or any other solution, as a PR.