[sbom] cache license resolver + VEX auto-extraction across aggregator runs#27589
Merged
qiluo-msft merged 1 commit intoMay 29, 2026
Merged
Conversation
bhouse-nexthop
requested review from
lguohan,
qiluo-msft and
xumia
as code owners
May 28, 2026 22:51
Collaborator
|
/azp run Azure.sonic-buildimage |
|
Azure Pipelines successfully started running 1 pipeline(s). |
…r runs Memoize the two SBOM aggregator sub-steps whose output is byte- identical across the per-variant invocations of `build_sbom.py` that the original SBOM PR (sonic-net#27455) introduced. For a multi-ASIC platform like broadcom (broadcom / broadcom-dnx / broadcom-legacy-th), the aggregator runs three times per build and currently redoes the same deterministic work twice. License resolver cache ====================== `build_sbom.py:resolve_licenses()` calls `scripts/sbom_resolve_licenses.py` against every `copyrights.tar.gz` under `target/versions/`. The copyrights tarballs are emitted per-container by `collect_version_files`, and most containers are byte-identical across ASIC variants — so the resolver was producing the same name→SPDX map roughly three times at ~75s each, ~210s of cumulative work deterministic from identical inputs. The cache mirrors the syft scanner cache pattern from sonic-net#27455: * Cache dir: `target/sbom-tools/license-cache/<sha>.json`, sibling to `target/sbom-tools/syft-cache/`. Lives under `target/`, so `make reset` invalidates it automatically. * Key: SHA-256 over the sorted SHA-256s of every input `copyrights.tar.gz`. The resolver's output is a pure function of those tarballs' content, so this is the right invalidator — any drift in any tarball forces a re-resolve. * Hit path: load cached resolver output and return; skips the python3 subprocess + DEP-5 walk + licensecheck fallback + SPDX-map lookup entirely. * Miss path: run the resolver normally, persist its full output dict if it produced a non-empty resolved map. Empty maps are not cached because they're indistinguishable from resolver failures. Expected savings: ~150s (~2.5 min) on a 3-variant build. VEX auto-extraction self-cache ============================== `scripts/sbom_extract_vex_from_patches.py` scans `src/**/*.patch` for CVE markers and emits OpenVEX `not_affected` statements under `vex/auto/`. It's invoked by `build_sbom.sh` at the start of every aggregator run; for a 3-variant build that's three identical passes producing byte-identical output (~13s + ~13s + ~4s today). The fix is a script-level self-cache rather than a separate cache directory: * Fingerprint: SHA-256 over the sorted (path, content-sha256) pairs of every discovered `*.patch` under `src/`. * Marker: `<output-dir>/.input_hash`. Lives inside `vex/auto/` so `rm -rf vex/auto/` (and `make reset`'s `git clean -xfdf`) invalidate it naturally — no separate cache dir, no separate cleanup story. * Hit: log "inputs unchanged" and exit 0 without rescanning. * Miss / first run: do the full walk and write the marker only after a successful pass. A failed run can't poison the cache. * Dry-run mode (`--dry-run`) is unaffected — it neither reads nor writes the marker. Expected savings: ~17-20s on a 3-variant build (variants 2+3 each skip ~10s of patch walking + file writing). Reproducibility =============== Both caches preserve byte-exact output: the SHA-256 keys are content-addressed and the underlying scripts produce deterministic output for identical input. `sbom_diff.py`'s reproducibility check continues to apply. Verification ============ Local smoke test on the VEX cache: two consecutive runs against the same tree produce one full scan + one "inputs unchanged since last run; vex/auto/ is fresh, skipping rescan." Touching any patch's content changes the fingerprint and forces a rescan on the next run. The license cache hits naturally in any 3-variant ENABLE_SBOM=y build — the first variant populates `target/sbom-tools/license-cache/<sha>.json`, the next two read it and skip the resolver subprocess. Cache files are JSON with a `resolved: {pkg → SPDX}` shape identical to what the resolver writes to `target/sbom-licenses.json`. Total expected savings: ~170s (~2.8 min) on a 3-variant build. Combined with the existing syft scanner cache from sonic-net#27455 (~3.5 min savings), the three caches eliminate roughly half of the per- variant aggregator redundancy. Signed-off-by: Brad House <bhouse@nexthop.ai>
bhouse-nexthop
force-pushed
the
bhouse.sbom-license-cache
branch
from
May 28, 2026 22:57
2f10047 to
e7a08e1
Compare
Collaborator
|
/azp run Azure.sonic-buildimage |
|
Azure Pipelines successfully started running 1 pipeline(s). |
Contributor
|
/azpw ms_conflict |
qiluo-msft
approved these changes
May 29, 2026
securely1g
pushed a commit
to securely1g/sonic-buildimage
that referenced
this pull request
Jun 4, 2026
…r runs (sonic-net#27589) Why I did it PR sonic-net#27455 (the opt-in SBOM PR) shipped a per-archive syft scanner cache that prevents the aggregator from re-scanning identical docker .gz files across the per-variant runs that fire for multi-ASIC platforms (e.g. broadcom invokes build_sbom.py three times — once each for broadcom, broadcom-dnx, broadcom-legacy-th). That cache addressed the largest single source of cross-variant duplication. Two smaller sources of cross-variant duplication remained: License resolution — scripts/sbom_resolve_licenses.py parses every per-container copyrights.tar.gz and emits a {package_name → SPDX expression} map. The input tarballs are byte-identical for the ~28 containers shared across ASIC variants, so the resolver was producing the same map roughly three times per build at ~75s each (~210s cumulative). VEX auto-extraction — scripts/sbom_extract_vex_from_patches.py walks src/**/*.patch for CVE markers and emits OpenVEX not_affected statements. It's invoked by build_sbom.sh at the start of every aggregator pass; the output is a pure function of the patch contents on disk, so variants 2 and 3 redid the same walk (~13s + ~4s per variant, ~30s cumulative). This PR adds caches for both. Same SHA-256-keyed content-addressed pattern as the syft cache, same make reset invalidation semantics, no new user-visible knobs. Total expected savings on a 3-variant build: ~170s (~2.8 min), which compounds with the existing ~3.5 min savings from the syft cache.
purush-nexthop
pushed a commit
to nexthop-ai/sonic-buildimage
that referenced
this pull request
Jun 10, 2026
…r runs (sonic-net#27589) Why I did it PR sonic-net#27455 (the opt-in SBOM PR) shipped a per-archive syft scanner cache that prevents the aggregator from re-scanning identical docker .gz files across the per-variant runs that fire for multi-ASIC platforms (e.g. broadcom invokes build_sbom.py three times — once each for broadcom, broadcom-dnx, broadcom-legacy-th). That cache addressed the largest single source of cross-variant duplication. Two smaller sources of cross-variant duplication remained: License resolution — scripts/sbom_resolve_licenses.py parses every per-container copyrights.tar.gz and emits a {package_name → SPDX expression} map. The input tarballs are byte-identical for the ~28 containers shared across ASIC variants, so the resolver was producing the same map roughly three times per build at ~75s each (~210s cumulative). VEX auto-extraction — scripts/sbom_extract_vex_from_patches.py walks src/**/*.patch for CVE markers and emits OpenVEX not_affected statements. It's invoked by build_sbom.sh at the start of every aggregator pass; the output is a pure function of the patch contents on disk, so variants 2 and 3 redid the same walk (~13s + ~4s per variant, ~30s cumulative). This PR adds caches for both. Same SHA-256-keyed content-addressed pattern as the syft cache, same make reset invalidation semantics, no new user-visible knobs. Total expected savings on a 3-variant build: ~170s (~2.8 min), which compounds with the existing ~3.5 min savings from the syft cache.
roger530-ho
pushed a commit
to roger530-ho/sonic-buildimage
that referenced
this pull request
Jun 23, 2026
…r runs (sonic-net#27589) Why I did it PR sonic-net#27455 (the opt-in SBOM PR) shipped a per-archive syft scanner cache that prevents the aggregator from re-scanning identical docker .gz files across the per-variant runs that fire for multi-ASIC platforms (e.g. broadcom invokes build_sbom.py three times — once each for broadcom, broadcom-dnx, broadcom-legacy-th). That cache addressed the largest single source of cross-variant duplication. Two smaller sources of cross-variant duplication remained: License resolution — scripts/sbom_resolve_licenses.py parses every per-container copyrights.tar.gz and emits a {package_name → SPDX expression} map. The input tarballs are byte-identical for the ~28 containers shared across ASIC variants, so the resolver was producing the same map roughly three times per build at ~75s each (~210s cumulative). VEX auto-extraction — scripts/sbom_extract_vex_from_patches.py walks src/**/*.patch for CVE markers and emits OpenVEX not_affected statements. It's invoked by build_sbom.sh at the start of every aggregator pass; the output is a pure function of the patch contents on disk, so variants 2 and 3 redid the same walk (~13s + ~4s per variant, ~30s cumulative). This PR adds caches for both. Same SHA-256-keyed content-addressed pattern as the syft cache, same make reset invalidation semantics, no new user-visible knobs. Total expected savings on a 3-variant build: ~170s (~2.8 min), which compounds with the existing ~3.5 min savings from the syft cache.
xdqi
pushed a commit
to canonical/sonic-buildimage
that referenced
this pull request
Jul 6, 2026
…r runs (sonic-net#27589) Why I did it PR sonic-net#27455 (the opt-in SBOM PR) shipped a per-archive syft scanner cache that prevents the aggregator from re-scanning identical docker .gz files across the per-variant runs that fire for multi-ASIC platforms (e.g. broadcom invokes build_sbom.py three times — once each for broadcom, broadcom-dnx, broadcom-legacy-th). That cache addressed the largest single source of cross-variant duplication. Two smaller sources of cross-variant duplication remained: License resolution — scripts/sbom_resolve_licenses.py parses every per-container copyrights.tar.gz and emits a {package_name → SPDX expression} map. The input tarballs are byte-identical for the ~28 containers shared across ASIC variants, so the resolver was producing the same map roughly three times per build at ~75s each (~210s cumulative). VEX auto-extraction — scripts/sbom_extract_vex_from_patches.py walks src/**/*.patch for CVE markers and emits OpenVEX not_affected statements. It's invoked by build_sbom.sh at the start of every aggregator pass; the output is a pure function of the patch contents on disk, so variants 2 and 3 redid the same walk (~13s + ~4s per variant, ~30s cumulative). This PR adds caches for both. Same SHA-256-keyed content-addressed pattern as the syft cache, same make reset invalidation semantics, no new user-visible knobs. Total expected savings on a 3-variant build: ~170s (~2.8 min), which compounds with the existing ~3.5 min savings from the syft cache.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why I did it
PR #27455 (the opt-in SBOM PR) shipped a per-archive syft scanner cache that prevents the aggregator from re-scanning identical docker
.gzfiles across the per-variant runs that fire for multi-ASIC platforms (e.g. broadcom invokesbuild_sbom.pythree times — once each for broadcom, broadcom-dnx, broadcom-legacy-th). That cache addressed the largest single source of cross-variant duplication.Two smaller sources of cross-variant duplication remained:
scripts/sbom_resolve_licenses.pyparses every per-containercopyrights.tar.gzand emits a{package_name → SPDX expression}map. The input tarballs are byte-identical for the ~28 containers shared across ASIC variants, so the resolver was producing the same map roughly three times per build at ~75s each (~210s cumulative).scripts/sbom_extract_vex_from_patches.pywalkssrc/**/*.patchfor CVE markers and emits OpenVEXnot_affectedstatements. It's invoked bybuild_sbom.shat the start of every aggregator pass; the output is a pure function of the patch contents on disk, so variants 2 and 3 redid the same walk (~13s + ~4s per variant, ~30s cumulative).This PR adds caches for both. Same SHA-256-keyed content-addressed pattern as the syft cache, same
make resetinvalidation semantics, no new user-visible knobs. Total expected savings on a 3-variant build: ~170s (~2.8 min), which compounds with the existing ~3.5 min savings from the syft cache.Work item tracking
How I did it
License resolver cache (
scripts/build_sbom.py:resolve_licenses)target/sbom-tools/license-cache/<sha>.json, sibling totarget/sbom-tools/syft-cache/from sbom: add opt-in SBOM generation and SBOM-based vulnerability scanning #27455. Lives undertarget/, somake resetinvalidates it naturally.copyrights.tar.gz. Content-addressed — any drift in any tarball forces a re-resolve.sbom_resolve_licenses.pynormally, persist the full output dict if it produced a non-emptyresolvedmap. Empty maps are not cached because they're indistinguishable from resolver failures and would otherwise poison subsequent variant runs._license_cache_dir()and_license_cache_key(), mirroring the existing_scanner_cache_dir()/_scanner_cache_lookup()helpers from sbom: add opt-in SBOM generation and SBOM-based vulnerability scanning #27455 for consistency.VEX auto-extraction self-cache (
scripts/sbom_extract_vex_from_patches.py)<output-dir>/.input_hashinsidevex/auto/itself. Co-locating the marker with the output it certifies meansrm -rf vex/auto/(andmake reset'sgit clean -xfdf) invalidate the cache naturally — no separate cache directory, no separate cleanup story.(path, content-sha256)pairs of every discovered*.patchundersrc/.inputs unchanged since last run; vex/auto/ is fresh, skipping rescan.and exit 0.--dry-run) is unaffected — it neither reads nor writes the marker.Reproducibility: both caches are content-addressed. The cached and freshly-computed outputs are byte-identical, so
scripts/sbom_diff.py's existing reproducibility check covers them.No new build flags, no new tools, no new dependencies. The caches activate automatically on any
ENABLE_SBOM=ybuild; they are transparent on non-SBOM builds (the caches never get touched).How to verify it
License cache:
VEX auto-extraction self-cache:
Reproducibility unchanged:
End-to-end:
make ENABLE_SBOM=y target/sonic-broadcom.binon master + this PR should produce the same 3 ASIC variants × full SBOM triplet (.cdx.json+.spdx.json+.intoto.json) asmasteralone, with no change in component counts or SBOM content. Only the aggregator wall-clock should differ.Which release branch to backport (provide reason below if selected)
This is a performance optimization on top of an opt-in feature, not a fix — no backport requested.
Tested branch (Please provide the tested image version)
Description for the changelog
Cache license-resolver and VEX auto-extraction output across per-variant
build_sbom.pyruns (~170s savings on a 3-variant ENABLE_SBOM=y build).Link to config_db schema for YANG module changes
N/A — no YANG schema changes.
A picture of a cute animal (not mandatory but encouraged)