From e8105cb30e0d479afe44018e31fbaabb0fcc3c5f Mon Sep 17 00:00:00 2001 From: sanasol Date: Wed, 28 Jan 2026 14:48:40 +0100 Subject: [PATCH] feat: add macOS code signing and notarization support - Add entitlements.mac.plist for hardened runtime - Add notarize.js post-sign hook for Apple notarization - Update package.json with signing config and @electron/notarize dep - Update GitHub Actions workflow with signing secrets Co-Authored-By: Claude Opus 4.5 --- .github/workflows/release.yml | 8 ++++++++ build/entitlements.mac.plist | 18 ++++++++++++++++++ package.json | 8 +++++++- scripts/notarize.js | 36 +++++++++++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 build/entitlements.mac.plist create mode 100644 scripts/notarize.js diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ee466e9..5123161 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -40,6 +40,14 @@ jobs: - run: npm ci - name: Build macOS Packages + env: + # Code signing + CSC_LINK: ${{ secrets.CSC_LINK }} + CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }} + # Notarization + APPLE_ID: ${{ secrets.APPLE_ID }} + APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }} + APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} run: npx electron-builder --mac --publish never - uses: actions/upload-artifact@v4 with: diff --git a/build/entitlements.mac.plist b/build/entitlements.mac.plist new file mode 100644 index 0000000..e7b78d2 --- /dev/null +++ b/build/entitlements.mac.plist @@ -0,0 +1,18 @@ + + + + + com.apple.security.cs.allow-jit + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.cs.disable-library-validation + + com.apple.security.network.client + + com.apple.security.network.server + + com.apple.security.files.user-selected.read-write + + + diff --git a/package.json b/package.json index c496c76..e9b2fbd 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ }, "license": "MIT", "devDependencies": { + "@electron/notarize": "^2.5.0", "electron": "^40.0.0", "electron-builder": "^26.4.0" }, @@ -131,8 +132,13 @@ } ], "icon": "build/icon.icns", - "category": "public.app-category.games" + "category": "public.app-category.games", + "hardenedRuntime": true, + "gatekeeperAssess": false, + "entitlements": "build/entitlements.mac.plist", + "entitlementsInherit": "build/entitlements.mac.plist" }, + "afterSign": "scripts/notarize.js", "nsis": { "oneClick": false, "allowToChangeInstallationDirectory": true, diff --git a/scripts/notarize.js b/scripts/notarize.js new file mode 100644 index 0000000..b8970ac --- /dev/null +++ b/scripts/notarize.js @@ -0,0 +1,36 @@ +const { notarize } = require('@electron/notarize'); +const path = require('path'); + +exports.default = async function notarizing(context) { + const { electronPlatformName, appOutDir } = context; + + // Only notarize macOS builds + if (electronPlatformName !== 'darwin') { + console.log('Skipping notarization: not macOS'); + return; + } + + // Skip if credentials not provided (local builds) + if (!process.env.APPLE_ID || !process.env.APPLE_APP_SPECIFIC_PASSWORD || !process.env.APPLE_TEAM_ID) { + console.log('Skipping notarization: missing credentials (APPLE_ID, APPLE_APP_SPECIFIC_PASSWORD, or APPLE_TEAM_ID)'); + return; + } + + const appName = context.packager.appInfo.productFilename; + const appPath = path.join(appOutDir, `${appName}.app`); + + console.log(`Notarizing ${appPath}...`); + + try { + await notarize({ + appPath, + appleId: process.env.APPLE_ID, + appleIdPassword: process.env.APPLE_APP_SPECIFIC_PASSWORD, + teamId: process.env.APPLE_TEAM_ID, + }); + console.log('Notarization complete!'); + } catch (error) { + console.error('Notarization failed:', error); + throw error; + } +};