From 93814cf6a40c8be3c989010818931c48fd6731ab Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 16 Nov 2025 13:16:42 +0000 Subject: [PATCH] fix(ci): resolve Cosign digest truncation for multi-platform builds Fixed an issue where container image digests were being truncated when signing with Cosign, causing parsing errors like: "could not parse reference: ghcr.io/.../streamspace-ui@sha256:5807b1a7bfacd" Root cause: The build output digest from docker/build-push-action can be unreliable for multi-platform builds (linux/amd64,linux/arm64), sometimes resulting in truncated or malformed digest strings when interpolated in GitHub Actions expressions. Solution: Use 'docker buildx imagetools inspect' to reliably extract the manifest list digest after the image is pushed. This approach: - Works correctly for multi-platform builds - Gets the actual manifest digest from the registry - Includes better error handling and debugging output - Validates the full sha256 hash format Changes: - Updated all 3 image signing steps (controller, API, UI) - Updated all 3 SBOM attestation steps - Added validation and error messages for debugging - Removed fragile digest parsing from build output Fixes: Cosign signing failures for multi-platform container images --- .github/workflows/container-images.yml | 63 ++++++++++++++++++++------ 1 file changed, 48 insertions(+), 15 deletions(-) diff --git a/.github/workflows/container-images.yml b/.github/workflows/container-images.yml index 97a71f19..b1c90b8c 100644 --- a/.github/workflows/container-images.yml +++ b/.github/workflows/container-images.yml @@ -99,15 +99,24 @@ jobs: env: COSIGN_EXPERIMENTAL: "true" run: | - echo "Raw digest: ${{ steps.build.outputs.digest }}" - # Remove newlines/CRs and take first comma-separated token - DIGEST=$(echo "${{ steps.build.outputs.digest }}" | tr -d '\r\n' | sed 's/,.*//') + # Get the first tag from the metadata action + IMAGE_TAG=$(echo "${{ steps.meta.outputs.tags }}" | head -n1) + echo "Image tag: $IMAGE_TAG" + + # Use imagetools to get the manifest digest (works reliably for multi-platform builds) + DIGEST=$(docker buildx imagetools inspect "$IMAGE_TAG" --format '{{.Manifest.Digest}}') + echo "Extracted digest: $DIGEST" + + # Validate digest format if [[ ! "$DIGEST" =~ ^sha256:[0-9a-f]{64}$ ]]; then echo "ERROR: unexpected digest format: $DIGEST" + echo "Full imagetools output:" + docker buildx imagetools inspect "$IMAGE_TAG" exit 1 fi + IMAGE_REF="${{ env.IMAGE_PREFIX }}-controller@${DIGEST}" - echo "Image reference: $IMAGE_REF" + echo "Image reference for signing: $IMAGE_REF" cosign sign --yes "$IMAGE_REF" - name: Generate SBOM for Controller @@ -124,7 +133,9 @@ jobs: env: COSIGN_EXPERIMENTAL: "true" run: | - DIGEST=$(echo "${{ steps.build.outputs.digest }}" | tr -d '\r\n' | sed 's/,.*//') + IMAGE_TAG=$(echo "${{ steps.meta.outputs.tags }}" | head -n1) + DIGEST=$(docker buildx imagetools inspect "$IMAGE_TAG" --format '{{.Manifest.Digest}}') + echo "Using digest for SBOM attestation: $DIGEST" IMAGE_REF="${{ env.IMAGE_PREFIX }}-controller@${DIGEST}" cosign attest --yes --type spdxjson \ --predicate sbom-controller.spdx.json \ @@ -207,15 +218,24 @@ jobs: env: COSIGN_EXPERIMENTAL: "true" run: | - echo "Raw digest: ${{ steps.build.outputs.digest }}" - # Remove newlines/CRs and take first comma-separated token - DIGEST=$(echo "${{ steps.build.outputs.digest }}" | tr -d '\r\n' | sed 's/,.*//') + # Get the first tag from the metadata action + IMAGE_TAG=$(echo "${{ steps.meta.outputs.tags }}" | head -n1) + echo "Image tag: $IMAGE_TAG" + + # Use imagetools to get the manifest digest (works reliably for multi-platform builds) + DIGEST=$(docker buildx imagetools inspect "$IMAGE_TAG" --format '{{.Manifest.Digest}}') + echo "Extracted digest: $DIGEST" + + # Validate digest format if [[ ! "$DIGEST" =~ ^sha256:[0-9a-f]{64}$ ]]; then echo "ERROR: unexpected digest format: $DIGEST" + echo "Full imagetools output:" + docker buildx imagetools inspect "$IMAGE_TAG" exit 1 fi + IMAGE_REF="${{ env.IMAGE_PREFIX }}-api@${DIGEST}" - echo "Image reference: $IMAGE_REF" + echo "Image reference for signing: $IMAGE_REF" cosign sign --yes "$IMAGE_REF" - name: Generate SBOM for API @@ -232,7 +252,9 @@ jobs: env: COSIGN_EXPERIMENTAL: "true" run: | - DIGEST=$(echo "${{ steps.build.outputs.digest }}" | tr -d '\r\n' | sed 's/,.*//') + IMAGE_TAG=$(echo "${{ steps.meta.outputs.tags }}" | head -n1) + DIGEST=$(docker buildx imagetools inspect "$IMAGE_TAG" --format '{{.Manifest.Digest}}') + echo "Using digest for SBOM attestation: $DIGEST" IMAGE_REF="${{ env.IMAGE_PREFIX }}-api@${DIGEST}" cosign attest --yes --type spdxjson \ --predicate sbom-api.spdx.json \ @@ -315,15 +337,24 @@ jobs: env: COSIGN_EXPERIMENTAL: "true" run: | - echo "Raw digest: ${{ steps.build.outputs.digest }}" - # Remove newlines/CRs and take first comma-separated token - DIGEST=$(echo "${{ steps.build.outputs.digest }}" | tr -d '\r\n' | sed 's/,.*//') + # Get the first tag from the metadata action + IMAGE_TAG=$(echo "${{ steps.meta.outputs.tags }}" | head -n1) + echo "Image tag: $IMAGE_TAG" + + # Use imagetools to get the manifest digest (works reliably for multi-platform builds) + DIGEST=$(docker buildx imagetools inspect "$IMAGE_TAG" --format '{{.Manifest.Digest}}') + echo "Extracted digest: $DIGEST" + + # Validate digest format if [[ ! "$DIGEST" =~ ^sha256:[0-9a-f]{64}$ ]]; then echo "ERROR: unexpected digest format: $DIGEST" + echo "Full imagetools output:" + docker buildx imagetools inspect "$IMAGE_TAG" exit 1 fi + IMAGE_REF="${{ env.IMAGE_PREFIX }}-ui@${DIGEST}" - echo "Image reference: $IMAGE_REF" + echo "Image reference for signing: $IMAGE_REF" cosign sign --yes "$IMAGE_REF" - name: Generate SBOM for UI @@ -340,7 +371,9 @@ jobs: env: COSIGN_EXPERIMENTAL: "true" run: | - DIGEST=$(echo "${{ steps.build.outputs.digest }}" | tr -d '\r\n' | sed 's/,.*//') + IMAGE_TAG=$(echo "${{ steps.meta.outputs.tags }}" | head -n1) + DIGEST=$(docker buildx imagetools inspect "$IMAGE_TAG" --format '{{.Manifest.Digest}}') + echo "Using digest for SBOM attestation: $DIGEST" IMAGE_REF="${{ env.IMAGE_PREFIX }}-ui@${DIGEST}" cosign attest --yes --type spdxjson \ --predicate sbom-ui.spdx.json \