fix(cd): signed-artifact-gate får retry på "no signatures found" (sign-racet mätt live) #60
+41
-17
@@ -112,31 +112,55 @@ jobs:
|
||||
# the artifact release.yml produces instead. Bounded: a genuinely
|
||||
# broken/skipped release run fails this job loudly rather than
|
||||
# hanging forever.
|
||||
#
|
||||
# The loop retries TWO distinct not-ready states (measured live on
|
||||
# run 2265dbb 2026-09-15: deploy pulled the image at 02:36:45 and
|
||||
# failed verify at 02:37:03 with "no signatures found" while the
|
||||
# release run attached the signature at 02:37:11 — an 8-second race
|
||||
# that the pull-only retry missed):
|
||||
# 1. tag not pushed yet (release still building)
|
||||
# 2. tag pushed but signature not attached yet (release between
|
||||
# push and cosign sign)
|
||||
attempt=0
|
||||
max_attempts=40
|
||||
until docker pull "$IMAGE_REF" >/tmp/pull.log 2>&1; do
|
||||
digest=""
|
||||
while true; do
|
||||
attempt=$((attempt + 1))
|
||||
if [ "$attempt" -ge "$max_attempts" ]; then
|
||||
echo "::error::no signed image found at $IMAGE_REF after $max_attempts attempts (10 min) — release.yml has not produced an artifact for sha-$SHORT. Refusing to deploy an unverified rebuild."
|
||||
cat /tmp/pull.log
|
||||
echo "::error::no signed image found at $IMAGE_REF after $max_attempts attempts (10 min) — release.yml has not produced a signed artifact for sha-$SHORT. Refusing to deploy an unverified rebuild."
|
||||
cat /tmp/pull.log 2>/dev/null || true
|
||||
cat /tmp/verify.log 2>/dev/null || true
|
||||
exit 1
|
||||
fi
|
||||
echo "image not yet available at $IMAGE_REF (attempt $attempt/$max_attempts) — waiting for release.yml..."
|
||||
sleep 15
|
||||
if ! docker pull "$IMAGE_REF" >/tmp/pull.log 2>&1; then
|
||||
echo "image not yet available at $IMAGE_REF (attempt $attempt/$max_attempts) — waiting for release.yml..."
|
||||
sleep 15
|
||||
continue
|
||||
fi
|
||||
|
||||
# Read back the digest the registry actually stored for this tag —
|
||||
# same filter release.yml itself uses (RepoDigests can hold multiple
|
||||
# entries; never trust index 0) — and verify THAT digest, never the
|
||||
# mutable tag alone, so a tag reassigned after signing can't slip past.
|
||||
repo_digest="$(docker inspect --format='{{json .RepoDigests}}' "$IMAGE_REF" \
|
||||
| node -e "const rd=JSON.parse(require('fs').readFileSync(0,'utf8')); const prefix=process.env.IMAGE_REPO+'@'; const m=rd.find(r=>r.startsWith(prefix)); if(!m){console.error('no RepoDigests entry starting with '+prefix+' among: '+JSON.stringify(rd));process.exit(1);} console.log(m);")"
|
||||
digest="${repo_digest##*@}"
|
||||
echo "$digest" | grep -Eq '^sha256:[0-9a-f]{64}$' || { echo "::error::unexpected digest shape: $digest"; exit 1; }
|
||||
|
||||
echo "verifying cosign signature on ${IMAGE_REPO}@${digest} ..."
|
||||
if cosign verify --key cosign.pub "${IMAGE_REPO}@${digest}" >/tmp/verify.log 2>&1; then
|
||||
break
|
||||
fi
|
||||
if grep -q "no signatures found" /tmp/verify.log; then
|
||||
echo "image present but signature not attached yet (attempt $attempt/$max_attempts) — waiting for release.yml's cosign sign..."
|
||||
sleep 15
|
||||
continue
|
||||
fi
|
||||
echo "::error::cosign verify FAILED for ${IMAGE_REPO}@${digest} (not a not-ready state — refusing to deploy)"
|
||||
cat /tmp/verify.log
|
||||
exit 1
|
||||
done
|
||||
|
||||
# Read back the digest the registry actually stored for this tag —
|
||||
# same filter release.yml itself uses (RepoDigests can hold multiple
|
||||
# entries; never trust index 0) — and verify THAT digest, never the
|
||||
# mutable tag alone, so a tag reassigned after signing can't slip past.
|
||||
repo_digest="$(docker inspect --format='{{json .RepoDigests}}' "$IMAGE_REF" \
|
||||
| node -e "const rd=JSON.parse(require('fs').readFileSync(0,'utf8')); const prefix=process.env.IMAGE_REPO+'@'; const m=rd.find(r=>r.startsWith(prefix)); if(!m){console.error('no RepoDigests entry starting with '+prefix+' among: '+JSON.stringify(rd));process.exit(1);} console.log(m);")"
|
||||
digest="${repo_digest##*@}"
|
||||
echo "$digest" | grep -Eq '^sha256:[0-9a-f]{64}$' || { echo "::error::unexpected digest shape: $digest"; exit 1; }
|
||||
|
||||
echo "verifying cosign signature on ${IMAGE_REPO}@${digest} ..."
|
||||
cosign verify --key cosign.pub "${IMAGE_REPO}@${digest}"
|
||||
|
||||
echo "digest=$digest" >> "$GITHUB_OUTPUT"
|
||||
echo "SIGNED ARTIFACT VERIFIED: ${IMAGE_REPO}@${digest} — authorizing Coolify redeploy of sha-$SHORT"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user