From 3f182d244138468686445bfaadf68bb8b59278c9 Mon Sep 17 00:00:00 2001 From: Joshua Zivkovic Date: Mon, 9 Feb 2026 09:43:29 +0000 Subject: [PATCH] Update cargo source to use source provenance --- src/buildstream_plugins/sources/cargo.py | 51 ++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/src/buildstream_plugins/sources/cargo.py b/src/buildstream_plugins/sources/cargo.py index c0e78d3..f22edad 100644 --- a/src/buildstream_plugins/sources/cargo.py +++ b/src/buildstream_plugins/sources/cargo.py @@ -74,6 +74,10 @@ *version_type*, for which it reports the checksum of the archive as the *version*. The versions extracted from the Cargo.lock at tracking time are used to report the *guess_version*. + +Additional source info can be specified using the `provenance` field for each referenced crate as per +`built-in source provenance documentation +`_. """ import json @@ -125,13 +129,14 @@ # sha (str|None): The sha256 checksum of the downloaded crate # class Crate(SourceFetcher): - def __init__(self, cargo, name, version, sha=None): + def __init__(self, cargo, name, version, sha=None, provenance=None): super().__init__() self.cargo = cargo self.name = name self.version = str(version) self.sha = sha + self.provenance = provenance self.mark_download_url(cargo.url) ######################################################## @@ -159,7 +164,12 @@ def fetch(self, alias_override=None, **kwargs): def get_source_info(self): url, _ = self._get_url() return self.cargo.create_source_info( - url, SourceInfoMedium.REMOTE_FILE, SourceVersionType.SHA256, self.sha, version_guess=self.version + url, + SourceInfoMedium.REMOTE_FILE, + SourceVersionType.SHA256, + self.sha, + version_guess=self.version, + provenance_node=self.provenance, ) ######################################################## @@ -368,16 +378,27 @@ class CargoSource(Source): # We need the Cargo.lock file to construct our ref at track time BST_REQUIRES_PREVIOUS_SOURCES_TRACK = True + BST_CUSTOM_SOURCE_PROVENANCE = True + ######################################################## # Plugin/Source API method implementations # ######################################################## def configure(self, node): - # The url before any aliasing # self.original_url = node.get_str("url", "https://static.crates.io/crates") self.cargo_lock = node.get_str("cargo-lock", "Cargo.lock") self.vendor_dir = node.get_str("vendor-dir", "crates") + self.provenance = {} + + # Because source provenance info lives in the ref it would be + # removed upon source track, create a backup here so it is + # available to be added back in when setting the ref + for crate in node.get_sequence("ref"): + if "provenance" in crate: + crate_name = crate.get_str("name") + provenance = crate.get_mapping("provenance") + self.provenance[crate_name] = provenance.strip_node_info() # If the specified URL is just an alias, require the alias to resolve # to a URL with a trailing slash. Otherwise, append a trailing slash if @@ -397,7 +418,14 @@ def preflight(self): return def get_unique_key(self): - return [self.original_url, self.cargo_lock, self.vendor_dir, self.ref] + return ( + [ + self.original_url, + self.cargo_lock, + self.vendor_dir, + list(self._drop_provenance_field(self.ref)), + ], + ) def is_resolved(self): return (self.ref is not None) and all(crate.is_resolved() for crate in self.crates) @@ -413,6 +441,14 @@ def get_ref(self): return self.ref def set_ref(self, ref, node): + def add_provenance(crate): + if crate["name"] in self.provenance: + crate["provenance"] = self.provenance[crate["name"]] + return crate + + # add provenance back in + ref = list(map(add_provenance, ref)) + node["ref"] = ref self._recompute_crates(node.get_sequence("ref")) @@ -513,10 +549,17 @@ def _parse_crates(self, refs): crate.get_str("name"), crate.get_str("version"), sha=crate.get_str("sha", None), + provenance=crate.get_mapping("provenance", None), ) for crate in refs ] + def _drop_provenance_field(self, refs): + for crate in refs.copy(): + if crate.get("provenance", None) is not None: + del crate["provenance"] + yield crate + def setup(): return CargoSource