From 492d06f64f3f23ebe52f9661ce2c4e431238a31e Mon Sep 17 00:00:00 2001 From: Viktor Petersson Date: Sat, 7 Mar 2026 08:09:35 +0000 Subject: [PATCH] Fix docker/chainguard dedup by checking component SBOMs instead of release artifacts The previous dedup logic searched for the image digest in release artifacts, but artifacts may live in the "latest" release rather than the versioned one. Now checks directly if the component already has an SBOM with the digest as version. Also simplifies cleanup to delete old SBOMs from the component. Co-Authored-By: Claude Opus 4.6 --- .github/workflows/sbom-builder.yml | 11 ++---- scripts/lib/sbomify-api.sh | 60 ++++++++++-------------------- 2 files changed, 23 insertions(+), 48 deletions(-) diff --git a/.github/workflows/sbom-builder.yml b/.github/workflows/sbom-builder.yml index ddb715d..a6fe207 100644 --- a/.github/workflows/sbom-builder.yml +++ b/.github/workflows/sbom-builder.yml @@ -259,13 +259,10 @@ jobs: steps.config.outputs.component_id != '' && !inputs.dry_run && (steps.config.outputs.source_type == 'docker' || steps.config.outputs.source_type == 'chainguard') - && steps.config.outputs.product_id != '' run: | source scripts/lib/common.sh source scripts/lib/sbomify-api.sh if sbomify_digest_exists \ - "${{ steps.config.outputs.product_id }}" \ - "${{ steps.config.outputs.version }}" \ "${{ steps.config.outputs.component_id }}" \ "${{ steps.digest.outputs.image_digest }}"; then echo "Digest already exists, skipping upload" @@ -319,18 +316,16 @@ jobs: UPLOAD: true PRODUCT_RELEASE: ${{ steps.config.outputs.product_release }} - - name: Cleanup old release artifacts + - name: Cleanup old SBOMs if: >- - steps.config.outputs.product_id != '' && !inputs.dry_run + steps.config.outputs.component_id != '' && !inputs.dry_run && steps.upload-decision.outputs.should_upload == 'true' && (steps.config.outputs.source_type == 'docker' || steps.config.outputs.source_type == 'chainguard') run: | source scripts/lib/common.sh source scripts/lib/sbomify-api.sh - sbomify_cleanup_old_artifacts \ - "${{ steps.config.outputs.product_id }}" \ - "${{ steps.config.outputs.version }}" \ + sbomify_cleanup_old_sboms \ "${{ steps.config.outputs.component_id }}" \ "${{ steps.digest.outputs.image_digest }}" env: diff --git a/scripts/lib/sbomify-api.sh b/scripts/lib/sbomify-api.sh index 714696f..1a39601 100644 --- a/scripts/lib/sbomify-api.sh +++ b/scripts/lib/sbomify-api.sh @@ -10,53 +10,33 @@ set -euo pipefail SBOMIFY_API="${SBOMIFY_API_URL:-https://app.sbomify.com}" -# Check if a release artifact already has this digest version -# Usage: sbomify_digest_exists +# Check if a component already has an SBOM with this digest as version +# Usage: sbomify_digest_exists # Returns: 0 if exists, 1 if not sbomify_digest_exists() { - local product_id="$1" tag_version="$2" component_id="$3" digest="$4" + local component_id="$1" digest="$2" - # Find the release - local releases release_id - releases=$(curl -fsSL -H "Authorization: Bearer ${SBOMIFY_TOKEN}" \ - "${SBOMIFY_API}/api/v1/releases?product_id=${product_id}&version=${tag_version}") - release_id=$(echo "$releases" | jq -r --arg v "$tag_version" \ - '.items[] | select(.version == $v) | .id' | head -1) - - [[ -z "$release_id" ]] && return 1 # No release → digest doesn't exist - - # Check artifacts for matching digest - local artifacts - artifacts=$(curl -fsSL -H "Authorization: Bearer ${SBOMIFY_TOKEN}" \ - "${SBOMIFY_API}/api/v1/releases/${release_id}/artifacts?mode=existing") - echo "$artifacts" | jq -e --arg cid "$component_id" --arg d "$digest" \ - '.items[] | select(.component_id == $cid and .sbom_version == $d)' > /dev/null 2>&1 + local sboms + sboms=$(curl -fsSL -H "Authorization: Bearer ${SBOMIFY_TOKEN}" \ + "${SBOMIFY_API}/api/v1/components/${component_id}/sboms") + echo "$sboms" | jq -e --arg d "$digest" \ + '.items[] | select(.sbom.version == $d)' > /dev/null 2>&1 } -# Remove old artifacts for a component from a release (keep only current digest) -# Usage: sbomify_cleanup_old_artifacts -sbomify_cleanup_old_artifacts() { - local product_id="$1" tag_version="$2" component_id="$3" current_digest="$4" - - # Find the release - local releases release_id - releases=$(curl -fsSL -H "Authorization: Bearer ${SBOMIFY_TOKEN}" \ - "${SBOMIFY_API}/api/v1/releases?product_id=${product_id}&version=${tag_version}") - release_id=$(echo "$releases" | jq -r --arg v "$tag_version" \ - '.items[] | select(.version == $v) | .id' | head -1) - - [[ -z "$release_id" ]] && return 0 # No release → nothing to clean +# Remove old SBOMs for a component (keep only current digest) +# Usage: sbomify_cleanup_old_sboms +sbomify_cleanup_old_sboms() { + local component_id="$1" current_digest="$2" - # Find old artifacts for this component with different digest - local old_ids - old_ids=$(curl -fsSL -H "Authorization: Bearer ${SBOMIFY_TOKEN}" \ - "${SBOMIFY_API}/api/v1/releases/${release_id}/artifacts?mode=existing" | \ - jq -r --arg cid "$component_id" --arg d "$current_digest" \ - '.items[] | select(.component_id == $cid and .sbom_version != $d) | .id') + local sboms old_ids + sboms=$(curl -fsSL -H "Authorization: Bearer ${SBOMIFY_TOKEN}" \ + "${SBOMIFY_API}/api/v1/components/${component_id}/sboms") + old_ids=$(echo "$sboms" | jq -r --arg d "$current_digest" \ + '.items[] | select(.sbom.version != $d) | .sbom.id') - for artifact_id in $old_ids; do - log_info "Removing old artifact ${artifact_id} from release ${release_id}" + for sbom_id in $old_ids; do + log_info "Removing old SBOM ${sbom_id} from component ${component_id}" curl -fsSL -X DELETE -H "Authorization: Bearer ${SBOMIFY_TOKEN}" \ - "${SBOMIFY_API}/api/v1/releases/${release_id}/artifacts/${artifact_id}" + "${SBOMIFY_API}/api/v1/sboms/sbom/${sbom_id}" || true done }