ci(release): pre-notarize entitlements gate#60
Conversation
Insert validation step between version-alignment check and tauri-action build. Fails fast — before any macOS-runner minutes are spent on signing or notarization — when: - com.apple.security.app-sandbox is present (the v0.2.0/v0.2.1 foot-gun) - any of the 3 required hardened-runtime carve-outs is missing (cs.allow-jit, cs.allow-unsigned-executable-memory, cs.disable-library-validation) Verified locally against real plist (pass) and against 3 mutations (sandbox added, cs.allow-jit removed, file missing) — all fail with the expected ::error:: annotation. Closes #53 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR adds a CI “pre-notarize” validation gate to the release workflow to catch macOS entitlements misconfigurations (notably the App Sandbox regression) before running the Tauri release/signing step.
Changes:
- Add a
Validate entitlementsstep that fails ifcom.apple.security.app-sandboxis present. - Require the three hardened-runtime carve-outs (
cs.allow-jit,cs.allow-unsigned-executable-memory,cs.disable-library-validation) to be present insrc-tauri/Entitlements.plist.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if grep -q 'com.apple.security.app-sandbox' "$PLIST"; then | ||
| echo "::error::App Sandbox must not be enabled for Developer ID distribution — it breaks the auto-updater. See #18/#41." | ||
| exit 1 | ||
| fi | ||
| for KEY in cs.allow-jit cs.allow-unsigned-executable-memory cs.disable-library-validation; do | ||
| if ! grep -q "com.apple.security.$KEY" "$PLIST"; then |
There was a problem hiding this comment.
grep patterns here are treated as regular expressions, so the . characters match any character. That means a malformed key like comXappleXsecurityXapp-sandbox would still be matched and fail the job even though the exact entitlement key isn't present. Use fixed-string matching (e.g., grep -Fq) or escape the dots so the check only triggers on the exact entitlement key string.
| if grep -q 'com.apple.security.app-sandbox' "$PLIST"; then | |
| echo "::error::App Sandbox must not be enabled for Developer ID distribution — it breaks the auto-updater. See #18/#41." | |
| exit 1 | |
| fi | |
| for KEY in cs.allow-jit cs.allow-unsigned-executable-memory cs.disable-library-validation; do | |
| if ! grep -q "com.apple.security.$KEY" "$PLIST"; then | |
| if grep -Fq 'com.apple.security.app-sandbox' "$PLIST"; then | |
| echo "::error::App Sandbox must not be enabled for Developer ID distribution — it breaks the auto-updater. See #18/#41." | |
| exit 1 | |
| fi | |
| for KEY in cs.allow-jit cs.allow-unsigned-executable-memory cs.disable-library-validation; do | |
| if ! grep -Fq "com.apple.security.$KEY" "$PLIST"; then |
| exit 1 | ||
| fi | ||
| for KEY in cs.allow-jit cs.allow-unsigned-executable-memory cs.disable-library-validation; do | ||
| if ! grep -q "com.apple.security.$KEY" "$PLIST"; then |
There was a problem hiding this comment.
The required-key check also uses regex grep, so the . characters in com.apple.security.$KEY match any character. This can produce false positives and allow a typo’d entitlement key to pass validation. Prefer fixed-string matching (e.g., grep -Fq "com.apple.security.$KEY") so the gate reliably enforces the exact entitlement keys.
| if ! grep -q "com.apple.security.$KEY" "$PLIST"; then | |
| if ! grep -Fq "com.apple.security.$KEY" "$PLIST"; then |
| - name: Validate entitlements | ||
| shell: bash | ||
| run: | | ||
| # Pre-notarize gate. Catches release-blocker plist misconfigs BEFORE | ||
| # burning macOS-runner minutes on signing + notarization. | ||
| # Origin: v0.2.0/v0.2.1 sandbox incident (#18, #41). |
There was a problem hiding this comment.
This step runs inside publish-tauri, which is configured to runs-on: macos-latest. It will still consume macOS runner minutes even when failing fast. If the goal is to avoid spending macOS minutes on misconfigurations, consider moving version/entitlements validation into a separate ubuntu-latest job and gating the macOS matrix job with needs:.
Summary
release.ymlthat runs beforetauri-action(so no macOS-runner minutes are spent on signing/notarization when the build is already broken at config time).com.apple.security.app-sandbox(the v0.2.0/v0.2.1 foot-gun that silently broke the auto-updater).cs.allow-jit,cs.allow-unsigned-executable-memory,cs.disable-library-validation) — any missing one is a fail.Mirrors the existing inline-bash precedent already in
release.yml(Verify version alignment / Verify latest.json asset URLs).Test plan
Local verification ran the same logic as the new step against four cases — all four matched expectations:
Entitlements.plist(post-v0.2.3):exit 0, "Entitlements OK"exit 1, sandbox error annotation citing [CRITICAL] Verify auto-updater latest.json points at PeoplePartner repo, not HRCommand #18/ci: bump actions/checkout from 4 to 6 #41cs.allow-jitremoved:exit 1, missing-carve-out error annotationexit 1, not-found annotationWorkflow-level positive verification will land naturally on the next real release tag.
Why now
The v0.2.0 → v0.2.3 saga (closed #18 on 2026-04-27) lost ~30 minutes of diagnosis time and burned three tagged releases. A 23-line CI step would have caught the root cause in ~5 seconds.
Closes #53
🤖 Generated with Claude Code