fix(desktop): make macOS signing+notarization explicit and Windows signing optional

- mac.notarize=true + hardenedRuntime + entitlements so a signed CI release
  actually notarizes (gatekeeper smoke + Squirrel.Mac auto-update need it)
- entitlements grant disable-library-validation for the Bun sidecar/node-pty
- local unsigned build passes -c.mac.notarize=false so electron:package still
  works without an Apple account
- release signing-preflight now hard-requires only the Apple secrets; Windows
  cert is optional (unsigned NSIS still auto-updates, just SmartScreen warning)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
程序员阿江(Relakkes) 2026-06-03 13:57:22 +08:00
parent 303f2bdf0e
commit 85cf04e305
5 changed files with 58 additions and 4 deletions

View File

@ -63,18 +63,27 @@ jobs:
WIN_CSC_LINK: ${{ secrets.WINDOWS_CERTIFICATE }}
WIN_CSC_KEY_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
run: |
# macOS signing + notarization is mandatory: Squirrel.Mac auto-update and
# first-launch Gatekeeper approval both require a notarized Developer ID build.
missing=()
[ -n "$CSC_LINK" ] || missing+=("MACOS_CERTIFICATE")
[ -n "$CSC_KEY_PASSWORD" ] || missing+=("MACOS_CERTIFICATE_PASSWORD")
[ -n "$APPLE_ID" ] || missing+=("APPLE_ID")
[ -n "$APPLE_APP_SPECIFIC_PASSWORD" ] || missing+=("APPLE_APP_SPECIFIC_PASSWORD")
[ -n "$APPLE_TEAM_ID" ] || missing+=("APPLE_TEAM_ID")
[ -n "$WIN_CSC_LINK" ] || missing+=("WINDOWS_CERTIFICATE")
[ -n "$WIN_CSC_KEY_PASSWORD" ] || missing+=("WINDOWS_CERTIFICATE_PASSWORD")
if [ "${#missing[@]}" -gt 0 ]; then
printf '::error::Missing required release signing/notarization secrets: %s\n' "${missing[*]}"
printf '::error::Missing required macOS signing/notarization secrets: %s\n' "${missing[*]}"
exit 1
fi
# Windows signing is optional: an unsigned NSIS installer still auto-updates
# (electron-updater), it only triggers SmartScreen warnings. Warn, do not block,
# so releases can ship with an Apple Developer ID alone.
win_missing=()
[ -n "$WIN_CSC_LINK" ] || win_missing+=("WINDOWS_CERTIFICATE")
[ -n "$WIN_CSC_KEY_PASSWORD" ] || win_missing+=("WINDOWS_CERTIFICATE_PASSWORD")
if [ "${#win_missing[@]}" -gt 0 ]; then
printf '::warning::Windows signing secrets missing (%s): the Windows build will be unsigned. Auto-update still works, but users will see SmartScreen warnings.\n' "${win_missing[*]}"
fi
build:
needs:

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- Child/helper executables inherit the parent entitlements under hardened runtime. -->
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<key>com.apple.security.inherit</key>
<true/>
</dict>
</plist>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- Electron/Chromium needs JIT + writable executable memory under hardened runtime. -->
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<!-- The server sidecar relies on environment-variable injection (proxy, CLAUDE_*). -->
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
<true/>
<!-- Allow loading the node-pty native module and spawning the Bun sidecar binary,
which are not signed with this app's Developer ID. Required for the terminal
and server runtime to work under a notarized hardened-runtime build. -->
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
</dict>
</plist>

View File

@ -33,7 +33,12 @@
"dmg",
"zip"
],
"icon": "src-tauri/icons/icon.icns"
"icon": "src-tauri/icons/icon.icns",
"hardenedRuntime": true,
"gatekeeperAssess": false,
"entitlements": "build/entitlements.mac.plist",
"entitlementsInherit": "build/entitlements.mac.inherit.plist",
"notarize": true
},
"win": {
"target": "nsis",

View File

@ -113,6 +113,10 @@ echo "[build-macos-arm64] Cleaning empty dmg-builder cache directories..."
BUILDER_ARGS=(bunx electron-builder --mac "${MAC_TARGET_ARRAY[@]}" --arm64 --publish never)
if [[ "${SIGN_BUILD:-0}" != "1" ]]; then
export CSC_IDENTITY_AUTO_DISCOVERY=false
# package.json sets mac.notarize=true for the signed CI release path. A local
# unsigned build has no Developer ID credentials, so explicitly disable
# notarization here to keep `electron:package` working without an Apple account.
BUILDER_ARGS+=(-c.mac.notarize=false)
fi
if [[ "$#" -gt 0 ]]; then
BUILDER_ARGS+=("$@")