Windows builds are now automated via .github/workflows/build-windows.yml (signed with Azure Trusted Signing, published to GCS). macOS releases are still cut manually from a dev machine (yarn publish:mac), which relies on:
A Developer ID Application certificate in the local Keychain
A notarytool keychain profile named default
We should bring macOS to parity with Windows so releases are reproducible, don't depend on one laptop, and survive cert/profile changes.
Scope
New workflow .github/workflows/build-mac.yml running on macos-latest for both arm64 and x64.
Import Developer ID cert into a temporary keychain at job start (e.g. apple-actions/import-codesign-certs@v3).
Notarize via App Store Connect API key (.p8 + Key ID + Issuer) — env-var-based, not keychainProfile.
GCP auth via existing PROD_GKE_SERVICE_ACCOUNT_KEY secret; publish via @electron-forge/publisher-gcs.
Invalidate RELEASES.json cache for darwin/arm64 and darwin/x64 after publish.
Changes required
js/forge.config.ts
Switch osxNotarize to prefer env-var-based API-key credentials when present, falling back to the local keychainProfile: 'default' so yarn publish:mac from a dev machine keeps working:
osxSign: {} auto-discovers from the keychain and needs no change — it works in both the local keychain and a temp CI keychain.
js/package.json
Current publish:mac has an interactive read -r answer version-bump prompt that will hang CI. Open question — pick one:
A — drop the prompt; trust devs to bump package.json before pushing
B — trigger only on v* tags and assert the tag matches package.json version in CI
C — keep interactive publish:mac for local; add non-interactive publish:mac:ci
New GitHub secrets
APPLE_CERT_P12_BASE64 — base64 of the exported Developer ID .p12
APPLE_CERT_PASSWORD — the .p12 export password
APPLE_API_KEY_P8_BASE64 — base64 of the App Store Connect API key .p8
APPLE_API_KEY_ID — 10-char key ID
APPLE_API_ISSUER — issuer UUID
Gotchas
Notarization queue time is 2–15 min of billed runner time per arch.
.p12 export must include the private key, not just the cert.
If the API key is ever rotated, all secrets above change together.
Out of scope
Switching publisher-gcs → a signed-URL-based CDN invalidation strategy. The current gsutil setmeta approach is fine.
Staged rollouts — not supported by Squirrel.Mac either.
## Context
Windows builds are now automated via `.github/workflows/build-windows.yml` (signed with Azure Trusted Signing, published to GCS). macOS releases are still cut manually from a dev machine (`yarn publish:mac`), which relies on:
- A Developer ID Application certificate in the local Keychain
- A `notarytool` keychain profile named `default`
We should bring macOS to parity with Windows so releases are reproducible, don't depend on one laptop, and survive cert/profile changes.
## Scope
- New workflow `.github/workflows/build-mac.yml` running on `macos-latest` for both `arm64` and `x64`.
- Import Developer ID cert into a temporary keychain at job start (e.g. `apple-actions/import-codesign-certs@v3`).
- Notarize via App Store Connect API key (`.p8` + Key ID + Issuer) — env-var-based, not `keychainProfile`.
- GCP auth via existing `PROD_GKE_SERVICE_ACCOUNT_KEY` secret; publish via `@electron-forge/publisher-gcs`.
- Invalidate `RELEASES.json` cache for `darwin/arm64` and `darwin/x64` after publish.
## Changes required
### `js/forge.config.ts`
Switch `osxNotarize` to prefer env-var-based API-key credentials when present, falling back to the local `keychainProfile: 'default'` so `yarn publish:mac` from a dev machine keeps working:
```ts
osxNotarize: process.env.APPLE_API_KEY ? {
appleApiKey: process.env.APPLE_API_KEY,
appleApiKeyId: process.env.APPLE_API_KEY_ID!,
appleApiIssuer: process.env.APPLE_API_ISSUER!,
} : { keychainProfile: 'default' }
```
`osxSign: {}` auto-discovers from the keychain and needs no change — it works in both the local keychain and a temp CI keychain.
### `js/package.json`
Current `publish:mac` has an interactive `read -r answer` version-bump prompt that will hang CI. Open question — pick one:
- **A** — drop the prompt; trust devs to bump `package.json` before pushing
- **B** — trigger only on `v*` tags and assert the tag matches `package.json` version in CI
- **C** — keep interactive `publish:mac` for local; add non-interactive `publish:mac:ci`
### New GitHub secrets
- `APPLE_CERT_P12_BASE64` — base64 of the exported Developer ID `.p12`
- `APPLE_CERT_PASSWORD` — the `.p12` export password
- `APPLE_API_KEY_P8_BASE64` — base64 of the App Store Connect API key `.p8`
- `APPLE_API_KEY_ID` — 10-char key ID
- `APPLE_API_ISSUER` — issuer UUID
## Gotchas
- Notarization queue time is 2–15 min of billed runner time per arch.
- `.p12` export must include the private key, not just the cert.
- If the API key is ever rotated, all secrets above change together.
## Out of scope
- Switching publisher-gcs → a signed-URL-based CDN invalidation strategy. The current `gsutil setmeta` approach is fine.
- Staged rollouts — not supported by Squirrel.Mac either.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Context
Windows builds are now automated via
.github/workflows/build-windows.yml(signed with Azure Trusted Signing, published to GCS). macOS releases are still cut manually from a dev machine (yarn publish:mac), which relies on:notarytoolkeychain profile nameddefaultWe should bring macOS to parity with Windows so releases are reproducible, don't depend on one laptop, and survive cert/profile changes.
Scope
.github/workflows/build-mac.ymlrunning onmacos-latestfor botharm64andx64.apple-actions/import-codesign-certs@v3)..p8+ Key ID + Issuer) — env-var-based, notkeychainProfile.PROD_GKE_SERVICE_ACCOUNT_KEYsecret; publish via@electron-forge/publisher-gcs.RELEASES.jsoncache fordarwin/arm64anddarwin/x64after publish.Changes required
js/forge.config.tsSwitch
osxNotarizeto prefer env-var-based API-key credentials when present, falling back to the localkeychainProfile: 'default'soyarn publish:macfrom a dev machine keeps working:osxSign: {}auto-discovers from the keychain and needs no change — it works in both the local keychain and a temp CI keychain.js/package.jsonCurrent
publish:machas an interactiveread -r answerversion-bump prompt that will hang CI. Open question — pick one:package.jsonbefore pushingv*tags and assert the tag matchespackage.jsonversion in CIpublish:macfor local; add non-interactivepublish:mac:ciNew GitHub secrets
APPLE_CERT_P12_BASE64— base64 of the exported Developer ID.p12APPLE_CERT_PASSWORD— the.p12export passwordAPPLE_API_KEY_P8_BASE64— base64 of the App Store Connect API key.p8APPLE_API_KEY_ID— 10-char key IDAPPLE_API_ISSUER— issuer UUIDGotchas
.p12export must include the private key, not just the cert.Out of scope
gsutil setmetaapproach is fine.