555132e09646f0381ca732ab48b8579ee0fb5aa3 reverted BootZipCopyAction to use GregorianCalendar, but I still think this value might change depending on the local timezone. We need to review how we should actually set the date.
Comment From: scottfrederick
From issue #20927, which drove that change, I observed that files written to the archive by the Gradle plugin could have either the fixed timestamp of Feb 01 00:00:00 or the timestamp Jan 31 18:00:00 when using OffsetDateTime.
0 Fri Feb 01 00:00:00 CST 1980 org/
0 Fri Feb 01 00:00:00 CST 1980 org/springframework/
0 Fri Feb 01 00:00:00 CST 1980 org/springframework/boot/
0 Fri Feb 01 00:00:00 CST 1980 org/springframework/boot/loader/
5303 Fri Feb 01 00:00:00 CST 1980 org/springframework/boot/loader/ClassPathIndexFile.class
30287 Thu Jan 31 18:00:00 CST 1980 BOOT-INF/lib/spring-boot-jarmode-layertools-2.3.0.M4.jar
996 Thu Jan 31 18:00:00 CST 1980 BOOT-INF/classpath.idx
7339 Thu Jan 31 18:00:00 CST 1980 BOOT-INF/layers.idx
There is a six hour difference between the timestamps, and my local time is six hours offset from UTC.
When using GregorianCalendar the timestamps are consistent, but I can't yet explain the difference.
Comment From: scottfrederick
On further review, the timestamps for the loader files are unrelated to the timestamps for the layertools jar and index files. It just happens that they end up with the same TZ-specific value when using GregorianCalendar to generate the fixed date.
Gradle archive tasks have a preserveFileTimesamps property. When the property is true (the default), tasks should not manipulate timestamps when copying files. In the test cases mentioned above, preserveFileTimestamps=true. The Feb 01 00:00:00 timestamp on the loader files is not being set by the plugin, but is reflecting the timestamp of each archive entry when read using new ZipInputStream(getClass().getResourceAsStream("/META-INF/loader/spring-boot-loader.jar")) and copied to the output archive unchanged.
When preserveFileTimestamps=false the plugin fixes the timestamp of all files in the archive to the same value, as expected.
I'll un-revert the timestamp change to use OffsetDateTime so that the fixed date, when applied, is tied to UTC instead of the building machine's default TZ.
Comment From: scottfrederick
The Boot Maven plugin does not use its own constant for timestamps, but uses the value of project.build.outputTimestamp. We should consider whether we want this date to be TZ-independent also.
Comment From: wilkinsona
For Maven where you have to specify the timestamp, I think we should use whatever the user has configured. The timestamp can include a zone (as the example, 2019-10-02T08:04:00Z, in the docs does). I think that should be sufficient.
Comment From: scottfrederick
These are some questions for us to consider:
-
The Gradle plugin applies a fixed timestamp to
classpath.idxandlayers.idxwhen they are added to the archive. The Maven plugin does not apply a timestamp, so the index files get the current date/time. We should harmonize this behavior. Is the fixed timestamp the right approach for index files? -
If a fixed timestamp is applied to index files, each plugin will need to have a constant for the timestamp. The Gradle plugin currently has such a constant, the Maven plugin does not. Should we use a time zone independent timestamp (e.g. using
OffsetDateTime) to improve reproducibility across build machines, or a time zone specific timestamp (e.g. usingGregorianCalendar) to match what Gradle uses whenpreserveFileTimestamps=true?The Boot build instructs Gradle to fix the timestamps of the loader and layer tools jars, using Gradle's time zone specific timestamp. The timestamps on these files are preserved when they are copied to the fat jar. If the Gradle plugin uses a different timestamp from Gradle's default, then the timestamps on the loader classes and layer tools jar might be different from the timestamps on index files (unless the building machine happens to be in UTC).
Both plugins provide a means to fix the timestamps of all files in the archive. The Maven plugin requires the user to set the
project.build.outputTimestampproperty, so the date that gets applied is out of our control. The Gradle plugin requires the user to setpreserveFileTimestamps=true, and the archive tasks use a hard-coded value. The built-in Gradle tasks will apply the time zone specific date,bootJarwould override this value with our plugin's hard-coded date.
Comment From: scottfrederick
After further discussion, we've decided to:
- Use a current timestamp for
classpath.idxandlayers.idxfile when they are added to the archive, instead of applying a fixed timestamp as the Gradle plugin does currently. - Use the same
GregorianCalendartime zone specific fixed timestamp as Gradle when the Spring Boot Gradle plugin needs to apply a timestamp (i.e. whenpreserveFileTimestamps=true).- Add a test to verify that the Spring Boot Gradle plugin's fixed timestamp matches the Gradle timestamp (which is in an internal Gradle class).
- Apply a timestamp to the exploded loader directories and classes, and to the bundled layer-tools jar, that matches the build timestamp of either the plugin or the loader-tools jar.
Comment From: philwebb
I've raised an issue with the Gradle team to see if they want to switch as well.
Comment From: philwebb
It turns out the timezone specific date is used because the zip writer expects one. See https://github.com/gradle/gradle/issues/12895#event-3268197433
Comment From: michael-o
This issue still persists :-(
Comment From: michael-o
This should now be fixed by https://github.com/spring-projects/spring-boot/commit/998d59b7ac1a75b26634e4fd2843a7833e554840?
Comment From: wilkinsona
Perhaps partially. This issue discusses both Maven and Gradle and the changes in 998d59b are Maven-specific as the Gradle plugin does not use JarWriter.
Comment From: philwebb
I've aligned put the same "fix" in Gradle as Maven now so things are at least consistent.