Releases are built by the .github/workflows/release.yml workflow, triggered by v* tags or manual workflow_dispatch. The workflow builds for macOS (universal), Windows (x64), and Linux (x64), then publishes artifacts to Cloudflare R2.
Use the /release command to execute a full gitflow release.
macOS builds are signed with a Developer ID Application certificate. The signing identity and certificate are stored as GitHub secrets.
| Secret | Description |
|---|---|
MAC_CERTS |
Base64-encoded .p12 signing certificate |
MAC_CERTS_PASSWORD |
Password for the .p12 certificate |
APPLE_API_KEY |
App Store Connect API key (.p8 file content) |
APPLE_API_KEY_ID |
10-character Key ID from App Store Connect (3NFG76895G) |
APPLE_API_ISSUER |
Issuer UUID from App Store Connect |
APPLE_TEAM_ID |
Apple Team ID (D9674SJ8J4) |
apple-actions/import-codesign-certs@v3imports the .p12 into a temp keychain- The .p8 key content is written from
APPLE_API_KEYsecret to a temp file APPLE_API_KEYenv var is set to the file path (not content) fornotarytool --key- electron-builder signs the app, then submits to Apple for notarization via
notarytool
Local copies of signing keys are stored in the keys/ directory (gitignored):
keys/AuthKey_3NFG76895G.p8— App Store Connect API key
The .env file (also gitignored) stores credentials for local builds. See .env for the current values.
To check notarization status locally:
xcrun notarytool history \
--key keys/AuthKey_3NFG76895G.p8 \
--key-id 3NFG76895G \
--issuer 193e486e-1945-4dcd-9bb5-ae68d41441efNotarization is enabled. All CI builds are signed and submitted to Apple's notarization service automatically.
- electron-builder 26.8.1 + @electron/notarize 2.5.0
APPLE_API_KEYenv var = file path to .p8 (not content, not Key ID)APPLE_API_KEY_ID= 10-character Key IDAPPLE_API_ISSUER= Issuer UUIDmac.notarizein package.json only acceptstrueorfalse(not an object) in electron-builder 26.8.1@electron/notarizecallsnotarytool submit --waitwith no timeout — Apple delays can block CI indefinitely
The workflow has a skip_notarization input for manual dispatches. This passes -c.mac.notarize=false to electron-builder at build time, overriding whatever is in package.json.
The workflow sets DEBUG=electron-builder,electron-notarize*,electron-osx-sign* for verbose signing/notarization output.
Artifacts are uploaded to Cloudflare R2 via AWS CLI:
- Binaries (dmg, zip, exe, AppImage, deb, blockmap) →
s3://<bucket>/releases/with immutable caching - Metadata (latest*.yml) →
s3://<bucket>/releases/with no-cache headers
| Secret | Description |
|---|---|
R2_ACCESS_KEY_ID |
R2 access key |
R2_SECRET_ACCESS_KEY |
R2 secret key |
R2_ENDPOINT |
R2 endpoint URL |
R2_BUCKET |
Bucket name (canopy-updates) |
The hardened runtime entitlements are in build/entitlements.mac.plist:
com.apple.security.cs.allow-jit— required for Electron with hardened runtimecom.apple.security.cs.allow-unsigned-executable-memory— may not be needed for Electron 40+, review when re-enabling notarizationcom.apple.security.cs.disable-library-validation— allows loading node-pty native module
To build locally without Developer ID signing or notarization (e.g. for testing), first compile the app then package it with signing disabled. On macOS (Unix shell only):
npm run build && CSC_IDENTITY_AUTO_DISCOVERY=false npx electron-builder --publish never -c.mac.notarize=false -c.mac.forceCodeSigning=falseNotes:
npm run buildis required first — electron-builder packages whatever is indist/anddist-electron/, so skipping it produces a stale or broken bundle.CSC_IDENTITY_AUTO_DISCOVERY=falsesuppresses auto-discovery of local Developer ID certificates. IfCSC_LINK,CSC_NAME, orAPPLE_*variables are exported in your shell, unset them as well.- The
CSC_IDENTITY_AUTO_DISCOVERY=falseinline syntax is POSIX-only (bash/zsh). On Windows useset CSC_IDENTITY_AUTO_DISCOVERY=false(cmd) or$env:CSC_IDENTITY_AUTO_DISCOVERY='false'(PowerShell) before the build command. - On macOS, universal and arm64 builds still apply ad-hoc signing even with these flags — the resulting app will not be Gatekeeper-trusted, but it will launch on the machine it was built on.