Skip to content

fix(ci): ad-hoc sign macOS bundles so Gatekeeper stops reporting them as damaged - #4

Merged
Locutusque merged 3 commits into
mainfrom
claude/macos-adhoc-signing
Aug 23, 2026
Merged

Locutusque merged 3 commits into
mainfrom
claude/macos-adhoc-signing

Conversation

@Locutusque

Copy link
Copy Markdown
Owner

Draft text — rewrite this before merging. Claude wrote everything below; per CONTRIBUTING.md the description should be in your words. The AI usage section is left untouched for you to fill in.

Which issue does this PR fix?

No filed issue. Reported directly: the v0.30.1 Apple Silicon DMG fails to open with "Donut" is damaged and can't be opened. You should move it to the Trash.

The DMG is not corrupt. Import Apple certificate exits early when the signing secrets are absent, leaving APPLE_SIGNING_IDENTITY unset. In tauri-bundler, an absent identity makes sign::keychain() return None, and macos/app.rs then skips the whole signing block — so the .app ships with no _CodeSignature at all. On Apple Silicon, Gatekeeper reads a quarantined bundle with no signature as corrupt rather than untrusted, and the resulting dialog offers only Move to Trash, with no Open Anyway.

This falls back to codesign's ad-hoc identity (-) when no certificate is configured. tauri_macos_sign::Keychain::with_signing_identity("-") sets path: None, so it runs a plain codesign --force -s - with no keychain lookup, and the bundler signs inside-out (sidecars, then nested binaries, then the bundle). Notarization stays skipped: with APPLE_ID/APPLE_PASSWORD/APPLE_TEAM_ID all unset, notarize_auth() returns MissingCredentials, which is a warning rather than an error. The signed path with real secrets is unchanged.

Applied to all three macOS-building workflows. build-installers.yml had no certificate step at all, so it gets a small if: matrix.family == 'macos' step.

Also corrects the documented quarantine path. productName is Donut, so the bundle is Donut.app; Donut Browser.app is the pre-rename name that app_auto_updater.rs cleans up, so the old xattr command silently targeted a path that does not exist.

How to test

  1. Run the Build Installers workflow (workflow_dispatch, platform macos) on this branch.
  2. Download the donut-aarch64-apple-darwin artifact and open the DMG on Apple Silicon.
  3. Before: "Donut" is damaged and can't be opened, Move to Trash only. After: the ordinary unverified-developer prompt, approvable via System Settings → Privacy & Security → Open Anyway.
  4. Optionally confirm the seal: codesign -dv --verbose=4 "/Volumes/Donut/Donut.app" should report Signature=adhoc instead of code object is not signed at all.

Known limitation: ad-hoc is not notarization. First launch still needs Open Anyway, and because an ad-hoc cdhash changes every build, TCC re-prompts for camera/microphone after each update. Setting the six APPLE_* secrets restores full Developer ID signing and notarization through the existing code path.

Checklist

  • Read CONTRIBUTING.md
  • Ran pnpm format && pnpm lint && pnpm test locally and it passes
  • I tested the changes myself by running the app locally
  • Updated translations in all locale files (if UI text changed)

Notes on the above, for you to confirm and tick yourself: pnpm format and pnpm lint were run and exit 0. pnpm test and the E2E suites were not run — no Rust or TypeScript changed, so no row of the E2E table in AGENTS.md applies. actionlint and typos are clean on all three workflows. The release-time sed marker rewrite was simulated locally to confirm the new README section sits outside <!-- install-links-* --> and survives. No user-facing strings changed, so no locale files are affected. Nothing here has been verified on real macOS hardware — step 2 above is the real test.

AI usage (required)

Tick exactly one. Ticking neither, ticking both, or deleting this section closes the PR automatically.

  • I did not use AI for any part of this PR
  • I used AI, and here is what it did:

Generated by Claude Code

Without a Developer ID certificate the bundler was handed no signing
identity at all, so it skipped signing entirely. A bundle with no
signature is not merely untrusted on Apple Silicon: Gatekeeper reads it
as corrupt, and a quarantined download reports "Donut is damaged and
can't be opened" with only Move to Trash on offer.

Fall back to codesign's ad-hoc identity when no certificate is
configured. The bundler signs sidecars and nested binaries before the
app bundle, and notarization stays skipped because APPLE_ID and friends
remain unset.

Also correct the documented quarantine path: the bundle is Donut.app,
not the pre-rename Donut Browser.app.
Same fix for the tagged and nightly release paths: when no Developer ID
certificate is configured, hand the bundler codesign's ad-hoc identity
instead of leaving APPLE_SIGNING_IDENTITY unset, which made it skip
signing entirely and produce a bundle Gatekeeper reads as corrupt.
@Locutusque
Locutusque merged commit 9298f2b into main Aug 23, 2026
20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant