|
1 | | -name: CI/CD Pipeline |
| 1 | +name: Public Pipeline |
2 | 2 |
|
3 | 3 | # CI/CD workflow for building, publishing, mirroring, signing container images and building release binaries. |
4 | 4 | # Actions are pinned to specific SHAs to reduce supply-chain risk. This workflow triggers on tag push events. |
@@ -440,6 +440,10 @@ jobs: |
440 | 440 | issuer="https://token.actions.githubusercontent.com" |
441 | 441 | id_regex="^https://github.com/${{ github.repository }}/.+" # accept this repo (all workflows/refs) |
442 | 442 |
|
| 443 | + # Track failures |
| 444 | + FAILED_TAGS=() |
| 445 | + SUCCESSFUL_TAGS=() |
| 446 | +
|
443 | 447 | # Determine if this is an RC release |
444 | 448 | IS_RC="false" |
445 | 449 | if [[ "$TAG" == *"-rc."* ]]; then |
@@ -471,94 +475,123 @@ jobs: |
471 | 475 | for BASE_IMAGE in "${GHCR_IMAGE}" "${DOCKERHUB_IMAGE}"; do |
472 | 476 | for IMAGE_TAG in "${IMAGE_TAGS[@]}"; do |
473 | 477 | echo "Processing ${BASE_IMAGE}:${IMAGE_TAG}" |
| 478 | + TAG_FAILED=false |
| 479 | +
|
| 480 | + # Wrap the entire tag processing in error handling |
| 481 | + ( |
| 482 | + set -e |
| 483 | + DIGEST="$(skopeo inspect --retry-times 3 docker://${BASE_IMAGE}:${IMAGE_TAG} | jq -r '.Digest')" |
| 484 | + REF="${BASE_IMAGE}@${DIGEST}" |
| 485 | + echo "Resolved digest: ${REF}" |
| 486 | +
|
| 487 | + echo "==> cosign sign (keyless) --recursive ${REF}" |
| 488 | + cosign sign --recursive "${REF}" |
| 489 | +
|
| 490 | + echo "==> cosign sign (key) --recursive ${REF}" |
| 491 | + cosign sign --key env://COSIGN_PRIVATE_KEY --recursive "${REF}" |
| 492 | +
|
| 493 | + # Retry wrapper for verification to handle registry propagation delays |
| 494 | + retry_verify() { |
| 495 | + local cmd="$1" |
| 496 | + local attempts=6 |
| 497 | + local delay=5 |
| 498 | + local i=1 |
| 499 | + until eval "$cmd"; do |
| 500 | + if [ $i -ge $attempts ]; then |
| 501 | + echo "Verification failed after $attempts attempts" |
| 502 | + return 1 |
| 503 | + fi |
| 504 | + echo "Verification not yet available. Retry $i/$attempts after ${delay}s..." |
| 505 | + sleep $delay |
| 506 | + i=$((i+1)) |
| 507 | + delay=$((delay*2)) |
| 508 | + # Cap the delay to avoid very long waits |
| 509 | + if [ $delay -gt 60 ]; then delay=60; fi |
| 510 | + done |
| 511 | + return 0 |
| 512 | + } |
| 513 | +
|
| 514 | + echo "==> cosign verify (public key) ${REF}" |
| 515 | + if retry_verify "cosign verify --key env://COSIGN_PUBLIC_KEY '${REF}' -o text"; then |
| 516 | + VERIFIED_INDEX=true |
| 517 | + else |
| 518 | + VERIFIED_INDEX=false |
| 519 | + fi |
474 | 520 |
|
475 | | - DIGEST="$(skopeo inspect --retry-times 3 docker://${BASE_IMAGE}:${IMAGE_TAG} | jq -r '.Digest')" |
476 | | - REF="${BASE_IMAGE}@${DIGEST}" |
477 | | - echo "Resolved digest: ${REF}" |
478 | | -
|
479 | | - echo "==> cosign sign (keyless) --recursive ${REF}" |
480 | | - cosign sign --recursive "${REF}" |
481 | | -
|
482 | | - echo "==> cosign sign (key) --recursive ${REF}" |
483 | | - cosign sign --key env://COSIGN_PRIVATE_KEY --recursive "${REF}" |
484 | | -
|
485 | | - # Retry wrapper for verification to handle registry propagation delays |
486 | | - retry_verify() { |
487 | | - local cmd="$1" |
488 | | - local attempts=6 |
489 | | - local delay=5 |
490 | | - local i=1 |
491 | | - until eval "$cmd"; do |
492 | | - if [ $i -ge $attempts ]; then |
493 | | - echo "Verification failed after $attempts attempts" |
494 | | - return 1 |
495 | | - fi |
496 | | - echo "Verification not yet available. Retry $i/$attempts after ${delay}s..." |
497 | | - sleep $delay |
498 | | - i=$((i+1)) |
499 | | - delay=$((delay*2)) |
500 | | - # Cap the delay to avoid very long waits |
501 | | - if [ $delay -gt 60 ]; then delay=60; fi |
502 | | - done |
503 | | - return 0 |
504 | | - } |
505 | | -
|
506 | | - echo "==> cosign verify (public key) ${REF}" |
507 | | - if retry_verify "cosign verify --key env://COSIGN_PUBLIC_KEY '${REF}' -o text"; then |
508 | | - VERIFIED_INDEX=true |
509 | | - else |
510 | | - VERIFIED_INDEX=false |
511 | | - fi |
512 | | -
|
513 | | - echo "==> cosign verify (keyless policy) ${REF}" |
514 | | - if retry_verify "cosign verify --certificate-oidc-issuer '${issuer}' --certificate-identity-regexp '${id_regex}' '${REF}' -o text"; then |
515 | | - VERIFIED_INDEX_KEYLESS=true |
516 | | - else |
517 | | - VERIFIED_INDEX_KEYLESS=false |
518 | | - fi |
| 521 | + echo "==> cosign verify (keyless policy) ${REF}" |
| 522 | + if retry_verify "cosign verify --certificate-oidc-issuer '${issuer}' --certificate-identity-regexp '${id_regex}' '${REF}' -o text"; then |
| 523 | + VERIFIED_INDEX_KEYLESS=true |
| 524 | + else |
| 525 | + VERIFIED_INDEX_KEYLESS=false |
| 526 | + fi |
519 | 527 |
|
520 | | - # If index verification fails, attempt to verify child platform manifests |
521 | | - if [ "${VERIFIED_INDEX}" != "true" ] || [ "${VERIFIED_INDEX_KEYLESS}" != "true" ]; then |
522 | | - echo "Index verification not available; attempting child manifest verification for ${BASE_IMAGE}:${IMAGE_TAG}" |
523 | | - CHILD_VERIFIED=false |
524 | | -
|
525 | | - for ARCH in arm64 amd64; do |
526 | | - CHILD_TAG="${IMAGE_TAG}-${ARCH}" |
527 | | - echo "Resolving child digest for ${BASE_IMAGE}:${CHILD_TAG}" |
528 | | - CHILD_DIGEST="$(skopeo inspect --retry-times 3 docker://${BASE_IMAGE}:${CHILD_TAG} | jq -r '.Digest' || true)" |
529 | | - if [ -n "${CHILD_DIGEST}" ] && [ "${CHILD_DIGEST}" != "null" ]; then |
530 | | - CHILD_REF="${BASE_IMAGE}@${CHILD_DIGEST}" |
531 | | - echo "==> cosign verify (public key) child ${CHILD_REF}" |
532 | | - if retry_verify "cosign verify --key env://COSIGN_PUBLIC_KEY '${CHILD_REF}' -o text"; then |
533 | | - CHILD_VERIFIED=true |
534 | | - echo "Public key verification succeeded for child ${CHILD_REF}" |
| 528 | + # If index verification fails, attempt to verify child platform manifests |
| 529 | + if [ "${VERIFIED_INDEX}" != "true" ] || [ "${VERIFIED_INDEX_KEYLESS}" != "true" ]; then |
| 530 | + echo "Index verification not available; attempting child manifest verification for ${BASE_IMAGE}:${IMAGE_TAG}" |
| 531 | + CHILD_VERIFIED=false |
| 532 | +
|
| 533 | + for ARCH in arm64 amd64; do |
| 534 | + CHILD_TAG="${IMAGE_TAG}-${ARCH}" |
| 535 | + echo "Resolving child digest for ${BASE_IMAGE}:${CHILD_TAG}" |
| 536 | + CHILD_DIGEST="$(skopeo inspect --retry-times 3 docker://${BASE_IMAGE}:${CHILD_TAG} | jq -r '.Digest' || true)" |
| 537 | + if [ -n "${CHILD_DIGEST}" ] && [ "${CHILD_DIGEST}" != "null" ]; then |
| 538 | + CHILD_REF="${BASE_IMAGE}@${CHILD_DIGEST}" |
| 539 | + echo "==> cosign verify (public key) child ${CHILD_REF}" |
| 540 | + if retry_verify "cosign verify --key env://COSIGN_PUBLIC_KEY '${CHILD_REF}' -o text"; then |
| 541 | + CHILD_VERIFIED=true |
| 542 | + echo "Public key verification succeeded for child ${CHILD_REF}" |
| 543 | + else |
| 544 | + echo "Public key verification failed for child ${CHILD_REF}" |
| 545 | + fi |
| 546 | +
|
| 547 | + echo "==> cosign verify (keyless policy) child ${CHILD_REF}" |
| 548 | + if retry_verify "cosign verify --certificate-oidc-issuer '${issuer}' --certificate-identity-regexp '${id_regex}' '${CHILD_REF}' -o text"; then |
| 549 | + CHILD_VERIFIED=true |
| 550 | + echo "Keyless verification succeeded for child ${CHILD_REF}" |
| 551 | + else |
| 552 | + echo "Keyless verification failed for child ${CHILD_REF}" |
| 553 | + fi |
535 | 554 | else |
536 | | - echo "Public key verification failed for child ${CHILD_REF}" |
| 555 | + echo "No child digest found for ${BASE_IMAGE}:${CHILD_TAG}; skipping" |
537 | 556 | fi |
| 557 | + done |
538 | 558 |
|
539 | | - echo "==> cosign verify (keyless policy) child ${CHILD_REF}" |
540 | | - if retry_verify "cosign verify --certificate-oidc-issuer '${issuer}' --certificate-identity-regexp '${id_regex}' '${CHILD_REF}' -o text"; then |
541 | | - CHILD_VERIFIED=true |
542 | | - echo "Keyless verification succeeded for child ${CHILD_REF}" |
543 | | - else |
544 | | - echo "Keyless verification failed for child ${CHILD_REF}" |
545 | | - fi |
546 | | - else |
547 | | - echo "No child digest found for ${BASE_IMAGE}:${CHILD_TAG}; skipping" |
| 559 | + if [ "${CHILD_VERIFIED}" != "true" ]; then |
| 560 | + echo "Failed to verify index and no child manifests verified for ${BASE_IMAGE}:${IMAGE_TAG}" |
| 561 | + exit 1 |
548 | 562 | fi |
549 | | - done |
550 | | -
|
551 | | - if [ "${CHILD_VERIFIED}" != "true" ]; then |
552 | | - echo "Failed to verify index and no child manifests verified for ${BASE_IMAGE}:${IMAGE_TAG}" |
553 | | - exit 10 |
554 | 563 | fi |
555 | | - fi |
| 564 | + ) || TAG_FAILED=true |
556 | 565 |
|
557 | | - echo "✓ Successfully signed and verified ${BASE_IMAGE}:${IMAGE_TAG}" |
| 566 | + if [ "$TAG_FAILED" = "true" ]; then |
| 567 | + echo "⚠️ WARNING: Failed to sign/verify ${BASE_IMAGE}:${IMAGE_TAG}" |
| 568 | + FAILED_TAGS+=("${BASE_IMAGE}:${IMAGE_TAG}") |
| 569 | + else |
| 570 | + echo "✓ Successfully signed and verified ${BASE_IMAGE}:${IMAGE_TAG}" |
| 571 | + SUCCESSFUL_TAGS+=("${BASE_IMAGE}:${IMAGE_TAG}") |
| 572 | + fi |
558 | 573 | done |
559 | 574 | done |
560 | 575 |
|
561 | | - echo "All images signed and verified successfully!" |
| 576 | + # Report summary |
| 577 | + echo "" |
| 578 | + echo "==========================================" |
| 579 | + echo "Sign and Verify Summary" |
| 580 | + echo "==========================================" |
| 581 | + echo "Successful: ${#SUCCESSFUL_TAGS[@]}" |
| 582 | + echo "Failed: ${#FAILED_TAGS[@]}" |
| 583 | + echo "" |
| 584 | +
|
| 585 | + if [ ${#FAILED_TAGS[@]} -gt 0 ]; then |
| 586 | + echo "Failed tags:" |
| 587 | + for tag in "${FAILED_TAGS[@]}"; do |
| 588 | + echo " - $tag" |
| 589 | + done |
| 590 | + echo "" |
| 591 | + echo "⚠️ WARNING: Some tags failed to sign/verify, but continuing anyway" |
| 592 | + else |
| 593 | + echo "✓ All images signed and verified successfully!" |
| 594 | + fi |
562 | 595 | shell: bash |
563 | 596 |
|
564 | 597 | post-run: |
|
0 commit comments