From e0f0fad19201ff110b77e57f06f96241f10259d3 Mon Sep 17 00:00:00 2001 From: Alan Wizemann Date: Mon, 27 Apr 2026 11:40:16 +0200 Subject: [PATCH] fix(release): post-package verification + non-destructive recovery docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add codesign --verify --strict --deep + spctl --assess on the extracted distribution zip inside build_variant() so any seal regression introduced by ditto / staple / future pipeline tweaks fails the release before users see "damaged" errors. Document the non-destructive recovery path in README and explicitly warn against `xattr -rc` and `codesign --force --deep --sign -` (issue #49 — both corrupt Sparkle.framework's nested XPC service / Updater.app signatures even when the outer app remains intact). Co-Authored-By: Claude Opus 4.7 (1M context) --- README.md | 14 ++++++++++++++ scripts/release.sh | 15 +++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/README.md b/README.md index b7b192b..d8b0b4b 100644 --- a/README.md +++ b/README.md @@ -179,6 +179,20 @@ Download the latest build from [Releases](https://github.com/awizemann/scarf/rel Scarf checks for updates automatically on launch via [Sparkle](https://sparkle-project.org) and daily thereafter. You can disable automatic checks or trigger a manual check from **Settings → General → Updates** or the menu bar icon. +#### "Scarf.app is damaged" on first launch + +If Gatekeeper rejects the app on first launch (occasionally happens on macOS 14+ for zip-distributed apps depending on extraction tool + quarantine state), the bundle itself is fine — every release is verified to pass `codesign --verify --strict --deep` and `spctl --assess --type execute` before it ships. The fix is to **only remove the quarantine attribute**, never strip all xattrs or re-sign: + +```bash +# Recommended — non-destructive +xattr -d com.apple.quarantine /Applications/Scarf.app + +# Or extract with ditto instead of double-clicking the zip: +ditto -xk ~/Downloads/Scarf-vX.X.X-Universal.zip ~/Downloads/ +``` + +**Do not run `xattr -rc /Applications/Scarf.app`** — it strips codesign-related extended attributes and can break the bundle's seal. **Do not run `codesign --force --deep --sign - /Applications/Scarf.app`** — `--deep` ad-hoc re-signing is incompatible with Sparkle.framework's nested XPC services and `Updater.app` sub-bundle, and will corrupt the framework signature even if the outer app appears intact afterward. If a clean re-download + `xattr -d com.apple.quarantine` doesn't resolve the issue, please open an issue with `codesign --verify --verbose=4 --strict /Applications/Scarf.app` output captured **before** any mitigation attempts. + ### Build from Source ```bash diff --git a/scripts/release.sh b/scripts/release.sh index 8ac8e6b..4cf652b 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -188,6 +188,21 @@ build_variant() { log "[$label] Package $(basename "$out_zip")" ditto -c -k --keepParent "$app_path" "$out_zip" + + # Post-package verification: extract the actual distribution zip and confirm + # codesign + Gatekeeper still accept it. Catches any regression introduced by + # ditto / staple / future pipeline tweaks before users see "damaged" errors. + # See issue #49 — without this, a broken seal in Sparkle.framework or the + # outer bundle would only surface in user reports. + log "[$label] Post-package signature + Gatekeeper verification" + local verify_dir + verify_dir="$(mktemp -d)" + ditto -xk "$out_zip" "$verify_dir" + codesign --verify --strict --deep --verbose=4 "$verify_dir/Scarf.app" \ + || die "[$label] codesign --verify failed on packaged zip" + spctl --assess --type execute --verbose "$verify_dir/Scarf.app" \ + || die "[$label] spctl --assess failed on packaged zip" + rm -rf "$verify_dir" } UNIVERSAL_ZIP="$RELEASE_DIR/Scarf-v${VERSION}-Universal.zip"