Package: python3-distro-info
Version: 1.9
Severity: wishlist
Tags: patch
User: reproducible-bui...@lists.alioth.debian.org
Usertags: timestamps
Control: affects -1 developers-reference

Dear Maintainer,

I'm an occasional volunteer contributor to the Reproducible Builds[1] project,
and recently noticed that the developers-reference package failed to build
deterministically on the test build infrastructure for Debian.

The difference in the build output appears in Debian release codenames
retrieved using the distro_info module in the python3-distro-info package.

In particular, varying system clock times during comparative builds can cause
the release codenames returned by distro-info to differ, due to date-based
filtering logic that uses datetime.date.today (system clock date) as a default
value.

Please find attached a patch to begin using the stable SOURCE_DATE_EPOCH[3]
build timestamp, when configured, as a source of the default date filter,
enabling repeatable and deterministic build output.  (I'll also offer this as
a merge request on Salsa)

Regards,
James

[1] - https://reproducible-builds.org

[2] - https://sources.debian.org/src/distro-info/1.9/python/distro_info.py/#L129

[3] - https://reproducible-builds.org/docs/source-date-epoch/
>From 2807fa377f4192c2bc5d3e384e3e7ec4a9c018ea Mon Sep 17 00:00:00 2001
From: James Addison <j...@jp-hosting.net>
Date: Mon, 14 Oct 2024 21:10:32 +0100
Subject: [PATCH] python: add release filtering by SOURCE_DATE_EPOCH

---
 python/distro_info.py                       |  6 +++++-
 python/distro_info_test/test_distro_info.py | 17 +++++++++++++++++
 2 files changed, 22 insertions(+), 1 deletion(-)

diff --git a/python/distro_info.py b/python/distro_info.py
index 34f1299..5822580 100644
--- a/python/distro_info.py
+++ b/python/distro_info.py
@@ -124,7 +124,11 @@ class DistroInfo:
                     _get_date(row, "eol-server"),
                 )
                 self._releases.append(release)
-        self._date = datetime.date.today()
+        source_date_epoch = os.environ.get("SOURCE_DATE_EPOCH")
+        if source_date_epoch is not None:
+            self._date = datetime.date.fromtimestamp(int(source_date_epoch))
+        else:
+            self._date = datetime.date.today()
 
     @property
     def all(self) -> list[str]:
diff --git a/python/distro_info_test/test_distro_info.py 
b/python/distro_info_test/test_distro_info.py
index b9e1cc1..d2d1fd1 100644
--- a/python/distro_info_test/test_distro_info.py
+++ b/python/distro_info_test/test_distro_info.py
@@ -18,6 +18,7 @@
 
 import datetime
 import unittest
+from unittest.mock import patch
 
 from distro_info import DebianDistroInfo, UbuntuDistroInfo
 
@@ -91,6 +92,14 @@ class DebianDistroInfoTestCase(unittest.TestCase):  # 
pylint: disable=too-many-p
         unsupported = ["buzz", "rex", "bo", "hamm", "slink", "potato", 
"woody", "sarge", "etch"]
         self.assertEqual(self._distro_info.unsupported(self._date), 
unsupported)
 
+    @patch.dict("os.environ", {"SOURCE_DATE_EPOCH": "1500000000"})
+    def test_date_filtering(self) -> None:
+        """Test: filter supported Debian releases based on build date"""
+        supported = ['jessie', 'stretch', 'buster', 'sid', 'experimental']
+
+        distro_info = DebianDistroInfo()
+        self.assertEqual(distro_info.supported(), supported)
+
     def test_codename(self) -> None:
         """Test: Codename decoding"""
         self.assertIsNone(self._distro_info.codename("foobar"))
@@ -171,6 +180,14 @@ class UbuntuDistroInfoTestCase(unittest.TestCase):  # 
pylint: disable=too-many-p
         unsupported = ["warty", "hoary", "breezy", "edgy", "feisty", "gutsy", 
"intrepid", "jaunty"]
         self.assertEqual(self._distro_info.unsupported(self._date), 
unsupported)
 
+    @patch.dict("os.environ", {"SOURCE_DATE_EPOCH": "1500000000"})
+    def test_date_filtering(self) -> None:
+        """Test: filter supported Ubuntu releases based on build date"""
+        supported = ['trusty', 'xenial', 'yakkety', 'zesty', 'artful']
+
+        distro_info = UbuntuDistroInfo()
+        self.assertEqual(distro_info.supported(), supported)
+
     def test_current_unsupported(self) -> None:
         """Test: List all unsupported Ubuntu distributions today."""
         unsupported = {"warty", "hoary", "breezy", "edgy", "feisty", "gutsy", 
"intrepid", "jaunty"}
-- 
2.45.2

Reply via email to