Setting up macOS code signing + notarisation¶
Once you've enrolled in the Apple Developer Program ($99/yr), this guide walks through wiring up CI to produce a signed + notarised .dmg so users no longer see Gatekeeper's "unidentified developer" warning.
The build-macos.yml workflow is already structured for this — it auto-detects whether the secrets below are present and falls back to an unsigned dev preview if any are missing. Just paste the six secrets and the next push to a v* tag (or manual dispatch) produces a signed .dmg.
1. Generate a Developer ID Application certificate¶
On your Mac, open Keychain Access:
- Keychain Access → Certificate Assistant → Request a Certificate from a Certificate Authority…
- Email: your Apple ID email; Common Name: anything; Saved to disk.
- Sign in to https://developer.apple.com/account → Certificates, IDs & Profiles.
- Certificates → + → Developer ID Application.
- Upload the CSR you just saved; download the resulting
.cer. - Double-click the
.certo install it into Keychain Access. It pairs with the private key from step 1–2. - In Keychain Access, find the certificate (it shows as "Developer ID Application:
(TEAMID)" ). Right-click → Export → save asdeveloperid.p12. Pick a strong password; you'll paste this password asMACOS_CERTIFICATE_PWD.
2. Generate an app-specific password for notarytool¶
- Sign in to https://appleid.apple.com/account/manage.
- Sign-In and Security → App-Specific Passwords → Generate.
- Label:
yazses notarytool. Save the password — you can't view it again.
3. Find your team ID¶
Go to https://developer.apple.com/account → Membership Details. The 10-character alphanumeric code is MACOS_NOTARY_TEAM_ID.
4. Find your signing identity string¶
Look for the line that starts with the SHA1 then has "Developer ID Application: <Your Name> (TEAMID)". Copy the full quoted string (without the surrounding quotes) — that's MACOS_SIGNING_IDENTITY.
5. Encode the .p12 for GitHub¶
base64 -i developerid.p12 -o developerid.p12.b64
cat developerid.p12.b64 | pbcopy # copies to clipboard
6. Set the six repo secrets¶
Go to Settings → Secrets and variables → Actions → New repository secret and add all six:
| Secret name | Value |
|---|---|
MACOS_CERTIFICATE | The base64 string from step 5 (paste; long single line) |
MACOS_CERTIFICATE_PWD | The .p12 password from step 1 |
MACOS_NOTARY_APPLE_ID | The Apple ID email |
MACOS_NOTARY_PASSWORD | The app-specific password from step 2 |
MACOS_NOTARY_TEAM_ID | The 10-char team ID from step 3 |
MACOS_SIGNING_IDENTITY | The full identity string from step 4 |
7. Verify¶
Trigger the workflow manually:
Watch the run. The "Detect signing presence" step prints "Signing + notarisation will run." when all six are set. Failure modes:
- "errSecInternalComponent" → the keychain isn't unlocked or partition list isn't set; usually a
MACOS_CERTIFICATE_PWDmismatch. - "Invalid notarisation" / "submission rejected" → the entitlements in
packaging/macos/entitlements.plistare missing something Apple wants; read thexcrun notarytool log <submission-id>output linked in the run. - "The Developer ID Application identity is not valid for signing" → the
.p12exported only the public part of the cert. Re-export from Keychain Access selecting both the certificate and its private key.
Once the run goes green, the .dmg attached to the GitHub Release is signed + notarised. Users no longer need the right-click → Open dance.
Updating¶
The signing certificate is valid for 5 years. Renew via the Apple Developer portal when it expires; export a fresh .p12 and update only the MACOS_CERTIFICATE and MACOS_CERTIFICATE_PWD secrets.
App-specific passwords don't expire but are revocable from appleid.apple.com if compromised.