Skip to content

Commit 1b6537a

Browse files
committed
feat(build): add macOS code signing and notarization support
- Add GitHub Actions workflow for macOS builds (x64 and arm64) - Configure electron-builder with hardened runtime and entitlements - Add notarization script using @electron/notarize - Support both manual and tag-triggered releases with Apple App Store Connect API - Add macOS entitlements plists for JIT and library validation - Update .gitignore to preserve build/ directory entitlements
1 parent c37c40f commit 1b6537a

File tree

6 files changed

+119
-0
lines changed

6 files changed

+119
-0
lines changed

.github/workflows/release-mac.yml

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
name: Build/release (macOS)
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v*.*.*'
7+
workflow_dispatch:
8+
9+
permissions:
10+
contents: write
11+
12+
jobs:
13+
release:
14+
runs-on: macos-latest
15+
16+
env:
17+
APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }}
18+
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
19+
APPLE_API_ISSUER_ID: ${{ secrets.APPLE_API_ISSUER_ID }}
20+
21+
strategy:
22+
fail-fast: false
23+
matrix:
24+
arch: [x64, arm64]
25+
26+
steps:
27+
- name: Check out Git repository
28+
uses: actions/checkout@v4
29+
30+
- name: Setup Bun
31+
uses: oven-sh/setup-bun@v2
32+
with:
33+
bun-version: latest
34+
35+
- name: Install dependencies
36+
run: bun install --frozen-lockfile
37+
38+
- name: Import macOS signing certificate
39+
uses: apple-actions/import-codesign-certs@v3
40+
with:
41+
p12-file-base64: ${{ secrets.MAC_CERTS }}
42+
p12-password: ${{ secrets.MAC_CERTS_PASSWORD }}
43+
44+
- name: Prepare Apple notarization key
45+
if: ${{ env.APPLE_API_KEY != '' && env.APPLE_API_KEY_ID != '' && env.APPLE_API_ISSUER_ID != '' }}
46+
run: |
47+
printf "%s" "$APPLE_API_KEY" > "$RUNNER_TEMP/AuthKey.p8"
48+
echo "APPLE_API_KEY_PATH=$RUNNER_TEMP/AuthKey.p8" >> $GITHUB_ENV
49+
50+
- name: Build app
51+
run: bun run build
52+
53+
- name: Build (electron-builder)
54+
if: ${{ !startsWith(github.ref, 'refs/tags/v') }}
55+
run: bunx electron-builder --mac --${{ matrix.arch }} --publish never
56+
57+
- name: Build + publish (electron-builder)
58+
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
59+
run: bunx electron-builder --mac --${{ matrix.arch }} --publish always
60+
env:
61+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
62+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
63+
APPLE_API_KEY_ID: ${{ env.APPLE_API_KEY_ID }}
64+
APPLE_API_ISSUER_ID: ${{ env.APPLE_API_ISSUER_ID }}

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ build*
33
.eggs*
44
.vscode*
55

6+
!build/
7+
!build/entitlements.mac.plist
8+
!build/entitlements.mac.inherit.plist
9+
610
# Node.js
711
node_modules/
812
npm-debug.log*
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>com.apple.security.cs.allow-jit</key>
6+
<true/>
7+
<key>com.apple.security.cs.disable-library-validation</key>
8+
<true/>
9+
</dict>
10+
</plist>

build/entitlements.mac.plist

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>com.apple.security.cs.allow-jit</key>
6+
<true/>
7+
<key>com.apple.security.cs.disable-library-validation</key>
8+
<true/>
9+
</dict>
10+
</plist>

package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
"directories": {
3232
"output": "release"
3333
},
34+
"afterSign": "scripts/notarize.js",
3435
"files": [
3536
"src/main.js",
3637
"dist/**/*",
@@ -41,6 +42,10 @@
4142
"mac": {
4243
"category": "public.app-category.utilities",
4344
"icon": "icon.icns",
45+
"hardenedRuntime": true,
46+
"entitlements": "build/entitlements.mac.plist",
47+
"entitlementsInherit": "build/entitlements.mac.inherit.plist",
48+
"gatekeeperAssess": false,
4449
"target": [
4550
{
4651
"target": "dmg",
@@ -71,6 +76,7 @@
7176
"@babel/core": "^7.28.4",
7277
"@babel/preset-env": "^7.28.5",
7378
"@babel/preset-react": "^7.27.1",
79+
"@electron/notarize": "^2.5.0",
7480
"@electron-forge/cli": "^7.9.0",
7581
"@testing-library/jest-dom": "^6.9.1",
7682
"@testing-library/react": "^16.3.0",

scripts/notarize.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
const path = require('path');
2+
const { notarize } = require('@electron/notarize');
3+
4+
module.exports = async function notarizing(context) {
5+
if (process.platform !== 'darwin') return;
6+
7+
const appleApiKeyPath = process.env.APPLE_API_KEY_PATH;
8+
const appleApiKeyId = process.env.APPLE_API_KEY_ID;
9+
const appleApiIssuer = process.env.APPLE_API_ISSUER_ID;
10+
11+
if (!appleApiKeyPath || !appleApiKeyId || !appleApiIssuer) {
12+
return;
13+
}
14+
15+
const { appOutDir, packager } = context;
16+
const appName = packager.appInfo.productFilename;
17+
const appPath = path.join(appOutDir, `${appName}.app`);
18+
19+
await notarize({
20+
appPath,
21+
appleApiKey: appleApiKeyPath,
22+
appleApiKeyId,
23+
appleApiIssuer,
24+
});
25+
};

0 commit comments

Comments
 (0)