On Mon, Oct 12, 2026 at 5:53 AM <[email protected]> wrote:
> From: Daniel Turull <[email protected]> > > Record each package's release date in the releaseTime property of its > software_Package object, using the SOURCE_DATE_EPOCH already computed > for reproducible builds. > > Accuracy depends on how SOURCE_DATE_EPOCH was derived: exact for > git-tagged recipes, best-effort for tarball/http(s) sources. Some > Python sdists (e.g. cryptography, hypothesis, maturin) normalize all > file mtimes to a fixed placeholder, so their releaseTime reflects > packaging-tool behavior, not the real release date. > This one is tricky to me. I understand why this data is useful, but there are also projects like https://release-monitoring.org/ that likely provide this information (and are more flexible in how they provide it); Ideally, you could post-annotate a SBoM with this information. The problem is really that we don't have any way to correlate our SRC_URI files to that data. You _could_ perhaps use PN and PV, but if that doesn't match their database, it doesn't do any good (and, as stated earlier, you really care about the release date of the SRC_URI artifact). So, in reality this is probably the best option we have, unless we are willing to somehow annotate our SRC_URI files to something that can positively cross-reference https://release-monitoring.org/ and make sure it stays up to date. > > Reported-by: Mathieu Dubois-Briand <[email protected]> > AI-Generated: Uses Kiro with Claude Sonnet 5 > Signed-off-by: Daniel Turull <[email protected]> > --- > v2: > - Dropped all options per Joshua's feedback; read SDE_FILE directly. > - Dropped the redundant else: delattr(recipe, "releaseTime") branch. > - Selftest compares against SDE_FILE content directly instead of > SOURCE_DATE_EPOCH, which can diverge from it. > - Fixed a leak: recipes with no git checkout and no fetched source > had SDE_FILE holding only SOURCE_DATE_EPOCH_FALLBACK, showing a > bogus 2011-04-05T23:00:00Z releaseTime instead of none. > v3: > - Also run after do_unpack: do_deploy_source_date_epoch's setscene > shortcut can skip it, leaving SOURCE_DATE_EPOCH unset. > - get_release_date() reads SOURCE_DATE_EPOCH again instead of > SDE_FILE, now that they're guaranteed equivalent. > - test_release_date_source_date_epoch: switched to tar (base-files > has S == UNPACKDIR and never gets a real SOURCE_DATE_EPOCH). > - Added test_release_date_omitted_for_fallback_value. > v4: > - Per Richard's feedback, moved releaseTime from do_create_recipe_spdx > to do_create_spdx to keep the recipe-only task fetch-free. > - Per Joshua's suggestion added into each downloaded source > software_Package > - Updated selftests accordingly. > v5: > - test_release_date_source_date_epoch: force do_unpack before reading > SOURCE_DATE_EPOCH. Mathieu hit this on the autobuilder: with all tasks > restored from sstate do_unpack never runs, so SDE_FILE is missing and > SOURCE_DATE_EPOCH falls back, making the test compare the correct > releaseTime against the fallback date. > --- > meta/lib/oe/spdx30_tasks.py | 22 ++++++++++++++ > meta/lib/oeqa/selftest/cases/spdx.py | 44 ++++++++++++++++++++++++++++ > 2 files changed, 66 insertions(+) > > diff --git a/meta/lib/oe/spdx30_tasks.py b/meta/lib/oe/spdx30_tasks.py > index b6456a214a..dc82f21fb0 100644 > --- a/meta/lib/oe/spdx30_tasks.py > +++ b/meta/lib/oe/spdx30_tasks.py > @@ -36,6 +36,24 @@ def set_timestamp_now(d, o, prop): > delattr(o, prop) > > > +def get_release_date(d): > + """Resolve the release date to record in a package's releaseTime > property. > + > + Uses SOURCE_DATE_EPOCH, which by this point (do_create_spdx runs after > + do_unpack and do_deploy_source_date_epoch) reflects the source mtimes. > + Omits the fallback value since it is not a meaningful release date. > + > + Returns a datetime, or None if no release date should be recorded. > + """ > + source_date_epoch = d.getVar("SOURCE_DATE_EPOCH") > + if not source_date_epoch or source_date_epoch == d.getVar( > + "SOURCE_DATE_EPOCH_FALLBACK" > + ): > + return None > Recipes can also directly set SOURCE_DATE_EPOCH, in which case it should be ignored. This is why I originally suggested using the same functions that SOURCE_DATE_EPOCH uses (when it's not overridden) instead of using the variable. > + > + return datetime.fromtimestamp(int(source_date_epoch), tz=timezone.utc) > + > + > def add_license_expression( > d, objset, license_expression, license_data, search_objsets=[] > ): > @@ -441,6 +459,7 @@ def _enrich_source_package(d, dl, fd, file_name, > primary_purpose): > > def add_download_files(d, objset): > inputs = set() > + release_date = get_release_date(d) > > urls = d.getVar("SRC_URI").split() > fetch = bb.fetch.Fetch(urls, d) > @@ -504,6 +523,9 @@ def add_download_files(d, objset): > > _enrich_source_package(d, dl, fd, file_name, primary_purpose) > > + if release_date is not None: > + dl.releaseTime = release_date > + if fd.method.supports_checksum(fd): > for checksum_id in bb.fetch.CHECKSUM_LIST: > if checksum_id not in > oe.spdx30.HashAlgorithm.NAMED_INDIVIDUALS: > diff --git a/meta/lib/oeqa/selftest/cases/spdx.py > b/meta/lib/oeqa/selftest/cases/spdx.py > index 8285189382 <(828)%20518-9382>..fb1176fe66 100644 > --- a/meta/lib/oeqa/selftest/cases/spdx.py > +++ b/meta/lib/oeqa/selftest/cases/spdx.py > @@ -6,6 +6,7 @@ > > import textwrap > import hashlib > +from datetime import datetime, timezone > from oeqa.selftest.case import OESelftestTestCase > from oeqa.utils.commands import bitbake, get_bb_var, get_bb_vars > import oe.spdx30 > @@ -443,3 +444,46 @@ class SPDX30Check(SPDX3CheckBase, OESelftestTestCase): > r'\d', > f"Version '{version}' for package '{name}' should contain > digits" > ) > + > + def test_release_date_source_date_epoch(self): > + """releaseTime should be derived from SOURCE_DATE_EPOCH. > + > + This is recorded on the downloaded source software_Package objects > + produced by do_create_spdx (not the recipe-only > do_create_recipe_spdx > + task), since the release date can only be known once sources are > + available and SOURCE_DATE_EPOCH has been computed. > + """ > + # base-files has S == UNPACKDIR and never gets a real > + # SOURCE_DATE_EPOCH, so use tar, which unpacks a real tarball. > + objset = self.check_recipe_spdx( > + "tar", > + > "{DEPLOY_DIR_SPDX}/{SSTATE_PKGARCH}/builds/build-tar.spdx.json", > + ) > + > + # With all tasks restored from sstate do_unpack never runs, > leaving > + # SDE_FILE missing and SOURCE_DATE_EPOCH at its fallback value. > + bitbake("tar -c unpack -f") > + source_date_epoch = get_bb_var("SOURCE_DATE_EPOCH", "tar") > + expected = datetime.fromtimestamp(int(source_date_epoch), > tz=timezone.utc) > + > + pkg = None > + for candidate in objset.foreach_type(oe.spdx30.software_Package): > + if candidate.software_downloadLocation: > + pkg = candidate > + break > + > + self.assertIsNotNone(pkg, "Unable to find downloaded source > software_Package") > + self.assertEqual(pkg.releaseTime, expected) > + > + def test_release_date_omitted_without_downloaded_source(self): > + """releaseTime only applies to downloaded source packages; recipes > + with only local file:// sources have none, and get no > releaseTime.""" > + # base-files only has file:// sources, so it has no downloaded > + # source software_Package and thus no releaseTime anywhere. > + objset = self.check_recipe_spdx( > + "base-files", > + > "{DEPLOY_DIR_SPDX}/{MACHINE_ARCH}/builds/build-base-files.spdx.json", > + ) > + > + for candidate in objset.foreach_type(oe.spdx30.software_Package): > + self.assertIsNone(candidate.releaseTime) >
-=-=-=-=-=-=-=-=-=-=-=- Links: You receive all messages sent to this group. View/Reply Online (#247891): https://lists.openembedded.org/g/openembedded-core/message/247891 Mute This Topic: https://lists.openembedded.org/mt/121713319/21656 Group Owner: [email protected] Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub [[email protected]] -=-=-=-=-=-=-=-=-=-=-=-
