経緯
以前作ってCI上でビルド・署名・リリースを行うようにしておいたElectron製のアプリケーションで、
いつのまにか
can't be opened because Apple cannot check it for malicious software.
こういうメッセージが出るようになっていた。
「署名の期限が切れた覚えもないし(←あとで確認したら切れてたけど、これは直接の原因ではなかった)なんだろうなぁ」と思い、
理由を調べて直したので、その記録。
原因
MacOS 10.14.5以降、Appleの基準が厳しくなって、notarize(※公証の意味)という処理を通じて、
アプリに対して認める権限の範囲をより詳細に規定しなければいけなくなったらしい。
対処
※ ここから先は electron-builder
を使っている場合について書きます
※ 前提として、AppleのDeveloper IDを取得して、ビルド・署名・パッケージングする部分は出来ていることとします
1. entitlements.mac.plistの追加
以下のような感じで用意する。
<?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>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
</dict>
</plist>
2. electron-builder の設定の追加
configの mac
配下に下記を追加
-
hardenedRuntime
をtrue
に設定 -
entitlements
とentitlementsInherit
に 1で作成したplistへのパスを設定する -
gatekeeperAssess
をfalse
に設定
参考:
https://www.electron.build/configuration/mac
3. app-specific passwordの取得
Apple ID Account Page から app-specific passwordを生成します。
これは、自由に再生成・破棄できるAPIキーのようなものです。
秘匿情報なので、gitにコミットなどはしないように気をつけましょう。
参考:
https://support.apple.com/en-us/HT204397
4. notarizeスクリプトの用意
electron-notarizeを使うので、
まず npm install -D electron-notarize
します
そして、下記のようなスクリプトでnotarize処理を書きます。
const { notarize } = require('electron-notarize');
const appBundleId = 'electron-builderのconfigでappIdに設定している値と同じもの'
// 下記例は環境変数から読み取っていますが、適宜読み替えてください
const appleId = process.env['APPLEID'] // apple developer IDのemailアドレス
const appleIdPassword = process.env['APPLEID_PASSWORD'] // 3で取得した、app-specific password
exports.default = async function notarizing(context) {
const { electronPlatformName, appOutDir } = context;
if(!appleId) throw new Error('appleId is not set in the env')
if(!appleIdPassword) throw new Error('appleIdPassword is not set in the env')
if (electronPlatformName !== 'darwin') {
throw new Error(`Notarization failed. Expected platform is 'darwin' but you are on ${electronPlatformName}`)
}
const appName = context.packager.appInfo.productFilename;
const appPath = `${appOutDir}/${appName}.app`
console.log(`Notarizing ${appBundleId} found at ${appPath}`);
return await notarize({
appBundleId,
appPath,
appleId,
appleIdPassword
});
};
5. electron-builderのフックにnotarizeを設定
electron-builderではcodesign後に任意のスクリプトを実行できるフックが提供されているので、
4で作成したnotarizeを実行する。
"afterSign": "node /path/to/notarize.js"
対応完了
諸々対応後のelectron-builderの設定:
"build": {
"appId": "com.なんらかの.ID",
"productName": "アプリ名",
"afterSign": "./scripts/notarize.js",
"mac": {
"hardenedRuntime": true,
"gatekeeperAssess": false,
"entitlements": "build/entitlements.mac.plist",
"entitlementsInherit": "build/entitlements.mac.plist"
}
}
この状態で、electron-builder
を走らせると、パッケージングなどの処理の一環で無事にnotarize処理が行われ、
配布したアプリを開けるようになります。
ただし、アプリによっては追加の権限が必要になる場合もあるかもしれないので、その場合は、plistに必要な権限を追記してください。
ちなみに、notarize部分単体でも 5分ぐらいかかる ので、トータルのコマンド実行時間は結構長くなります。