From 4991f6377e871f54bdefb324cda0debb25606c54 Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Fri, 23 Oct 2026 17:11:00 -0400 Subject: [PATCH] packaging(macos): retry stapling when Apple's ticket lookup drops (#8995) * packaging(macos): retry stapling when Apple's ticket lookup drops The 0.15.0 release's macOS job failed after both notarizations were accepted: stapling the DMG lost its connection to Apple's CloudKit ticket service (error 68). Retry stapling up to five times with a growing pause before failing the job. Co-Authored-By: Claude Opus 5.5 * packaging(macos): retry stapler validation too The re-run of the 0.15.0 macOS job stapled the DMG, then lost the CloudKit connection in `stapler validate` (error 68 again). Retry staple + validate as a pair in package.sh, and retry the validation verify.sh does. Co-Authored-By: Claude Opus 5.5 --------- Co-authored-by: Claude Opus 5.5 --- packaging/macos/package.sh | 24 ++++++++++++++++++++---- packaging/macos/verify.sh | 13 ++++++++++++- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/packaging/macos/package.sh b/packaging/macos/package.sh index 1b0974a340..b4be928efd 100755 --- a/packaging/macos/package.sh +++ b/packaging/macos/package.sh @@ -97,6 +97,24 @@ notarize() { fi } +# Stapling and validating both look the ticket up in Apple's CloudKit service. Right after +# notarization that lookup can drop the connection ("The network connection was lost", error 68) +# even though the ticket exists, so retry the pair a few times before failing the release. +staple() { + local file="$1" attempt + for attempt in 1 2 3 4 5; do + if xcrun stapler staple "$file" && xcrun stapler validate "$file"; then + return + fi + if [ "$attempt" = 5 ]; then + echo "error: stapling $(basename "$file") failed after $attempt attempts" >&2 + exit 1 + fi + echo "stapling $(basename "$file") failed (attempt $attempt); retrying in $((attempt * 20))s" >&2 + sleep $((attempt * 20)) + done +} + # ---- PhotoCraft.app ---------------------------------------------------------------------------- echo "==> assembling $APP" mkdir -p "$APP/Contents/MacOS" "$APP/Contents/Resources" @@ -123,8 +141,7 @@ codesign --verify --strict --deep --verbose=2 "$APP" if [[ "$NOTARIZE" = 1 ]]; then ditto -c -k --keepParent "$APP" "$WORK/PhotoCraft-notarize.zip" notarize "$WORK/PhotoCraft-notarize.zip" - xcrun stapler staple "$APP" - xcrun stapler validate "$APP" + staple "$APP" spctl --assess --type execute -vvv "$APP" fi @@ -149,8 +166,7 @@ sign "$DMG" codesign --verify --strict --verbose=2 "$DMG" if [[ "$NOTARIZE" = 1 ]]; then notarize "$DMG" - xcrun stapler staple "$DMG" - xcrun stapler validate "$DMG" + staple "$DMG" spctl --assess --type open --context context:primary-signature -vvv "$DMG" fi diff --git a/packaging/macos/verify.sh b/packaging/macos/verify.sh index 7521d44e03..46f779c2cf 100755 --- a/packaging/macos/verify.sh +++ b/packaging/macos/verify.sh @@ -70,6 +70,17 @@ assess() { return 1 } +# `stapler validate` asks Apple's CloudKit service too, which sometimes drops the connection +# (error 68) though the ticket is there. +validate_staple() { + local attempt + for attempt in 1 2 3 4 5; do + xcrun stapler validate "$1" && return 0 + [[ "$attempt" = 5 ]] || sleep $((attempt * 20)) + done + return 1 +} + for f in "$DMG" "$CLI_ZIP"; do if [[ ! -f "$f" ]]; then echo "error: missing $f" >&2 @@ -126,7 +137,7 @@ fi echo "==> $(basename "$DMG")" codesign --verify --strict --verbose=2 "$DMG" if [[ "$STRICT" = 1 ]]; then - xcrun stapler validate "$DMG" || fail "no notarization ticket stapled to the DMG" + validate_staple "$DMG" || fail "no notarization ticket stapled to the DMG" assess --assess --type open --context context:primary-signature -vv "$DMG" \ || fail "Gatekeeper rejects the DMG" fi