From a4061f831e856ccb90e82d3f0ec3044c07ca21c7 Mon Sep 17 00:00:00 2001 From: Achilleas Koutsou Date: Wed, 2 Jul 2025 15:17:57 +0200 Subject: [PATCH] stages/dnf4.versionlock: respect SOURCE_DATE_EPOCH Respect the SOURCE_DATE_EPOCH environment variable for reproducible builds. https://reproducible-builds.org/docs/source-date-epoch/ --- stages/org.osbuild.dnf4.versionlock | 11 +++++++++-- stages/org.osbuild.dnf4.versionlock.meta.json | 4 +++- stages/test/test_dnf4_versionlock.py | 6 ++---- 3 files changed, 14 insertions(+), 7 deletions(-) mode change 100644 => 100755 stages/org.osbuild.dnf4.versionlock diff --git a/stages/org.osbuild.dnf4.versionlock b/stages/org.osbuild.dnf4.versionlock old mode 100644 new mode 100755 index 062da425..9deaabe8 --- a/stages/org.osbuild.dnf4.versionlock +++ b/stages/org.osbuild.dnf4.versionlock @@ -1,7 +1,8 @@ #!/usr/bin/python3 +import os import pathlib import sys -import time +from datetime import datetime, timezone import osbuild.api @@ -11,7 +12,13 @@ def main(tree, options): tree = pathlib.Path(tree) locklist = tree / "etc/dnf/plugins/versionlock.list" - nowstr = time.ctime() + + # respect SOURCE_DATE_EPOCH for the timestamp in the comment for reproducible builds + source_date_epoch = os.environ.get("SOURCE_DATE_EPOCH") + nowstr = datetime.now().ctime() + if source_date_epoch: + nowstr = datetime.fromtimestamp(int(source_date_epoch), tz=timezone.utc).ctime() + with locklist.open(mode="a", encoding="utf-8") as locklist_fp: for item in add: # the plugin adds an empty line followed by a comment with a timestamp above each item, let's replicate the diff --git a/stages/org.osbuild.dnf4.versionlock.meta.json b/stages/org.osbuild.dnf4.versionlock.meta.json index b7b61123..77bdcd95 100644 --- a/stages/org.osbuild.dnf4.versionlock.meta.json +++ b/stages/org.osbuild.dnf4.versionlock.meta.json @@ -8,7 +8,9 @@ "Notes:", " - This stage is only valid for dnf4 and will have no effect on distributions that use dnf5.", " - Items are written as is. This is unlike adding items by calling 'dnf versionlock add',", - " which uses the dnf cache to retrieve version information for the listed packages." + " which uses the dnf cache to retrieve version information for the listed packages.", + " - The stage respects SOURCE_DATE_EPOCH for reproducible builds, which affects the timestamps", + " that are included as comments in the file." ], "schema": { "additionalProperties": false, diff --git a/stages/test/test_dnf4_versionlock.py b/stages/test/test_dnf4_versionlock.py index 85a36a20..5f29dea9 100644 --- a/stages/test/test_dnf4_versionlock.py +++ b/stages/test/test_dnf4_versionlock.py @@ -34,6 +34,7 @@ def test_schema_validation_dnf4_versionlock(stage_schema, test_data, expected_er ({"add": ["proto-1:1.1", "deftero-0:2.2", "trito-3:3.3-3.fc33"]}), ]) def test_locklist_dnf4_versionlock(tmp_path, stage_module, test_data): + os.environ["SOURCE_DATE_EPOCH"] = "1554721380" plugins_dir = os.path.join(tmp_path, "etc/dnf/plugins/") locklist_path = os.path.join(plugins_dir, "versionlock.list") os.makedirs(plugins_dir) @@ -44,8 +45,5 @@ def test_locklist_dnf4_versionlock(tmp_path, stage_module, test_data): for idx, package in enumerate(test_data["add"]): assert locklist_data[idx * 3] == "\n" - - # let's ignore the timestamp, just check that the comment was written - assert locklist_data[idx * 3 + 1].startswith("# Added lock on") - + assert locklist_data[idx * 3 + 1] == "# Added lock on Mon Apr 8 11:03:00 2019\n" assert locklist_data[idx * 3 + 2] == package + "\n"