epub: Honor SOURCE_DATE_EPOCH for dcterms:modified and dc:identifier - #2256
Conversation
| case System.get_env("SOURCE_DATE_EPOCH") do | ||
| nil -> | ||
| :calendar.universal_time() | ||
|
|
||
| epoch -> | ||
| case Integer.parse(epoch) do | ||
| {seconds, ""} -> :calendar.system_time_to_universal_time(seconds, :second) | ||
| _ -> :calendar.universal_time() | ||
| end | ||
| end |
There was a problem hiding this comment.
| case System.get_env("SOURCE_DATE_EPOCH") do | |
| nil -> | |
| :calendar.universal_time() | |
| epoch -> | |
| case Integer.parse(epoch) do | |
| {seconds, ""} -> :calendar.system_time_to_universal_time(seconds, :second) | |
| _ -> :calendar.universal_time() | |
| end | |
| end | |
| with epoch when is_binary(epoch) <- System.get_env("SOURCE_DATE_EPOCH"), | |
| {seconds, ""} <- Integer.parse(epoch) do | |
| :calendar.system_time_to_universal_time(seconds, :second) | |
| else | |
| _ -> :calendar.universal_time() | |
| end |
| time = | ||
| with epoch when is_binary(epoch) <- System.get_env("SOURCE_DATE_EPOCH"), | ||
| {seconds, ""} <- Integer.parse(epoch) do | ||
| :calendar.system_time_to_universal_time(seconds, :second) | ||
| else | ||
| _ -> :calendar.universal_time() | ||
| end | ||
|
|
||
| {{year, month, day}, {hour, min, sec}} = time |
There was a problem hiding this comment.
| time = | |
| with epoch when is_binary(epoch) <- System.get_env("SOURCE_DATE_EPOCH"), | |
| {seconds, ""} <- Integer.parse(epoch) do | |
| :calendar.system_time_to_universal_time(seconds, :second) | |
| else | |
| _ -> :calendar.universal_time() | |
| end | |
| {{year, month, day}, {hour, min, sec}} = time | |
| {{year, month, day}, {hour, min, sec}} = | |
| with epoch when is_binary(epoch) <- System.get_env("SOURCE_DATE_EPOCH"), | |
| {seconds, ""} <- Integer.parse(epoch) do | |
| :calendar.system_time_to_universal_time(seconds, :second) | |
| else | |
| _ -> :calendar.universal_time() | |
| end | |
| <<a::binary-size(4), b::binary-size(2), c::binary-size(2), d::binary-size(2), | ||
| e::binary-size(6)>> = :erlang.md5("#{config.project} #{config.version}") |
There was a problem hiding this comment.
I wonder if we should either always default to config.project/config.version, but that is likely not randomic enough OR add the source_date_epoch here. Basically, we always build the datatime first, and use it to generate the datetime and the UUID.
There was a problem hiding this comment.
Adding SOURCE_DATE_EPOCH to the hash would mean we get extra diffs in the output for unrelated changes. That is OK, as it does not block verification with identical build inputs. But is there really an advantage in it?
Are there other relevant inputs to the epub output? Maybe those could be added to the hash as well.
There was a problem hiding this comment.
Good questions. I did a quick check and this is actually good. My suggestion is to always apply this new version, instead of conditionally.
|
📦 Docs artifacts are ready: https://github.com/elixir-lang/ex_doc/actions/runs/30304750221/artifacts/8667987990 |
…tifier The EPUB content.opf embedded the build time as dcterms:modified and a random UUID as dc:identifier, making the .epub files differ between two builds of the same docs. Derive the identifier from the project name and version instead of random data, and use SOURCE_DATE_EPOCH (https://reproducible-builds.org/docs/source-date-epoch/) as the modification time when it is set. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
b32b03c to
8f49898
Compare
|
💚 💙 💜 💛 ❤️ |
The EPUB
content.opfembedded the build time asdcterms:modifiedand a random UUID asdc:identifier, making the .epub files differ between two builds of the same docs.When
SOURCE_DATE_EPOCHis set(https://reproducible-builds.org/docs/source-date-epoch/), use it as the modification time, and derive the identifier from the project name and version instead of random data. Behavior without
SOURCE_DATE_EPOCHis unchanged.This PR was done while working on reproducible builds for openSUSE. Together with #2255, this makes our
erlang-docpackage build reproducible.