* chore(release): add macOS signing/Gatekeeper/notarization verification Codify and enforce the macOS build-signing requirements. The release pipeline built bundles and had opt-in Apple signing, but never verified codesign/spctl/notarization — unsigned or broken bundles could ship silently. - scripts/verify-macos-signing.sh: runs codesign --verify --deep --strict, spctl Gatekeeper assessment, per-nested-Mach-O signature check, stapler validate, and (opt-in) notarytool history. Report-only by default (unsigned dev/preview is expected); --require-signed fails on any unsigned/un-notarized component so a broken release stops instead of publishing an unsigned artifact. - scripts/macos-dev-unquarantine.sh: local-dev-only quarantine stripper, with a loud "never a substitute for notarization" warning. - release.yml: new "Verify macOS signing" step on the macOS leg — report-only on unsigned paths, STRICT on the opt-in signed stable path (same condition as "Configure Apple signing"), so signing/notarization failures fail the job. - docs/macos-signing-verification.md: the canonical 10-point requirements + how-to-verify checklist, cross-linked to docs/install/macos.md and DESKTOP_RELEASE.md. Verified locally: report-only PASS (exit 0) and --require-signed FAIL (exit 1) against the real unsigned debug .app; release.yml parses as valid YAML. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(macos): ad-hoc sign bundle so users open it without Terminal (no Apple ID) The "app is damaged and can't be opened" error is caused by a broken/incomplete code-signature seal (codesign --verify failed: "code has no resources but signature indicates they must be present") on the quarantined download — there is no GUI bypass for that variant on modern macOS, forcing users to run `xattr`. Give the bundle a VALID ad-hoc signature at build time (free, no Apple Developer account) via tauri.conf.json bundle.macOS.signingIdentity = "-". Verified through a real `tauri build`: the produced .app is now flags=adhoc,runtime and passes codesign --verify --deep --strict. A valid seal flips the Gatekeeper prompt from the un-bypassable "damaged" to the GUI-bypassable "unidentified developer", which users clear with right-click → Open / Settings → "Open Anyway" — no Terminal. Still not notarized (that needs the paid Apple ID), so there's a one-time confirmation rather than a clean double-click. The opt-in Developer-ID path is unchanged: APPLE_SIGNING_IDENTITY (env) overrides the "-" default on the signed stable release. - tauri.conf.json: signingIdentity "-" (ad-hoc default). - verify-macos-signing.sh: detect ad-hoc tier; report the no-Terminal GUI path in report-only, still FAIL it under --require-signed (production must notarize). - docs/install/macos.md: lead the Gatekeeper section with right-click → Open; keep xattr as fallback for the harsher "damaged"/corrupted-download case. - docs/macos-signing-verification.md: signing-tiers table + ad-hoc default note. - release.yml: comment the ad-hoc default + env override on the signed path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
47 lines
2.4 KiB
Bash
Executable File
47 lines
2.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# ──────────────────────────────────────────────────────────────────────────
|
|
# macos-dev-unquarantine.sh — strip com.apple.quarantine from a LOCAL TEST
|
|
# artifact so you can launch an unsigned dev/preview build without the
|
|
# right-click → Open dance.
|
|
#
|
|
# ⚠️ LOCAL DEVELOPMENT CONVENIENCE ONLY.
|
|
# This is NEVER a substitute for proper Developer ID signing + notarization
|
|
# of production releases. Real users must receive a signed, notarized build
|
|
# (see docs/macos-signing-verification.md) — do not ship artifacts and tell
|
|
# users to run this. Production signing is gated separately in release.yml.
|
|
#
|
|
# Usage:
|
|
# scripts/macos-dev-unquarantine.sh "path/to/OmniVoice Studio.app"
|
|
# scripts/macos-dev-unquarantine.sh ~/Downloads/OmniVoice*.dmg
|
|
# scripts/macos-dev-unquarantine.sh # auto-discover newest built .app
|
|
# ──────────────────────────────────────────────────────────────────────────
|
|
set -uo pipefail
|
|
|
|
if [ "$(uname -s)" != "Darwin" ]; then
|
|
echo "macos-dev-unquarantine: not macOS — nothing to do."
|
|
exit 0
|
|
fi
|
|
|
|
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
TARGET="${1:-}"
|
|
|
|
if [ -z "$TARGET" ]; then
|
|
TARGET="$(find "$REPO_ROOT/frontend/src-tauri/target" -type d -name '*.app' -path '*/bundle/macos/*' 2>/dev/null | grep -E '/release/' | head -1)"
|
|
[ -z "$TARGET" ] && TARGET="$(find "$REPO_ROOT/frontend/src-tauri/target" -type d -name '*.app' -path '*/bundle/macos/*' 2>/dev/null | head -1)"
|
|
[ -z "$TARGET" ] && { echo "ERROR: no built .app found — pass an explicit path." >&2; exit 2; }
|
|
fi
|
|
|
|
[ -e "$TARGET" ] || { echo "ERROR: path does not exist: $TARGET" >&2; exit 2; }
|
|
|
|
echo "⚠️ DEV-ONLY: stripping com.apple.quarantine from:"
|
|
echo " $TARGET"
|
|
echo " (not a substitute for signing + notarization — see docs/macos-signing-verification.md)"
|
|
|
|
xattr -dr com.apple.quarantine "$TARGET" 2>/dev/null || true
|
|
|
|
if xattr -pr com.apple.quarantine "$TARGET" >/dev/null 2>&1; then
|
|
echo "✗ quarantine attribute still present — try: sudo xattr -dr com.apple.quarantine \"$TARGET\""
|
|
exit 1
|
|
fi
|
|
echo "✓ quarantine cleared — the unsigned build will now launch locally."
|