Macでプラグイン (bundle) のインストーラーを作成する

## 課題 ##
現時点で最新のXcodeではアプリケーションに関してはNotarizationは自動でやってくれるようになったけど、プラグイン (bundle）に関しては自動でやってくれない。さらにはプラグイはインストーラー配布したいのでインストーラーを作成してそれも署名してNotarizationを受けないとインストールが簡単にならない。しないとどうなるか？ざっくりいうとまともにインストールできないのでみんなに使ってもらえない。

## 前提条件 ##
JUCEのProjucerを使ってAUやVSTなどのAudioプラグインをビルドしている。JUCE使わないでプラグインをつくってる人はたぶんこんなの自己解決してると思う。

## 必要なCertificateがあるか確認 ##
最新のXcodeではメニューから"Settings..."を選んで"Accounts"の右下あたりにある"Manage Certificates..."を選んでCertificateがあるかどうかを確認。有効期限内のDevelopper ID ApplicationとDevelopper ID InstallerがあればOK。無かったら左下にある"+"ボタンを押して"Developper ID Application"と"Developper ID Installer"を選べばサクッと作ってくれる。

ちなみに昔はdeveloper.apple.comにサインインし、Certificatesに下記があるか確認して、ダウンロードして、ダブルクリックしてKeychainに登録。Xcodeを起動し、Preference→Accounts→Manage Certificates...で存在と有効期限を確認とかしてた。

* Developer ID Application (Platform = macOS)
* Developer ID Installer (Platform = macOS)

## Bundle (AU/VST/AAX) をビルドする ##
Xcodeで各ターゲットを選択して、Signing & Capabilitiesで

* Automaticaly manage signingのチェックを外す
* Teamで自分のアカウントを選択
* Signing CertificateでDevelopper ID Applicationを選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107818/b3768c1b-f886-b591-87fb-6ab9a36972d0.png)

Product→Archiveでビルドする。ビルドできたら、OrganizerでDistribute Content→Custom→Built Productsして適当な場所に保存。

注) Standalone AppはAutomatically manage signingのままでOK！

…ってしなくてもProjucerのExporterの設定でCode-Signing IdentityにDeveloper ID Applicationと書けばOKだった。StandaloneもmanualになるけどOK。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107818/7860e1e9-4c5c-3cf5-24fb-a23ee23bb201.png)



## Flat pkgを作成する ##
pkgbuildでインストーラーを作成するには、インストールされるフォルダの階層をまず作って、ビルドしたプラグインを置いておく。VSTの場合はこんな感じになる。この一番上のVSTというのは任意の名前でいい。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107818/e79c3036-0ff9-67e1-6c91-276ac62aea79.png)

このフォルダを作ったのと同じ階層で下記のコマンドを使ってインストーラーを作成できる。
```
$ pkgbuild --root ./VST --identifier "your.bundle.id" --version "X.X.X" --sign "Developer ID Installer: Your Name (XXXXXXXXXX)" --timestamp "XXXFlat.pkg"
```
* your.bundle.idはプラグインのbundle ID
* X.X.Xはプラグインのバージョン番号
* Your Nameはチーム名
* XXXXXXXXXXはチーム ID
* XXXFlat.pkgはインストーラーの名前
* pkgbuildの後でproductbuildしてインストーラーを完成させてる例が多いが、手っ取り早く済ませたいならpkgbuildでflat packageを作るだけでも良い。ただし、一つのインストーラーでAU版とVST版のインストールを選択可能にするなどしたければproductbuildが必要。
* --signと--timestampは必須で、この2つが正しくないとNotarizationが通らない。

```
$ pkgbuild --root "Your Folder" --identifier "your.bundle.id" --version "X.X.X" --sign "Developer ID Installer: Your Name (XXXXXXXXXX)" --timestamp "XXXFlat.pkg"
pkgbuild: Inferring bundle components from contents of "フォルダ"
pkgbuild: Adding component at Library/Audio/Plug-Ins/Components/XXX.component
pkgbuild: Signing package with identity "Developer ID Installer: Your Name (XXXXXXXXXX)" from keychain /Users/xxx/Library/Keychains/login.keychain-db
pkgbuild: Adding certificate "Apple Worldwide Developer Relations Certification Authority"
pkgbuild: Adding certificate "Apple Root CA"
pkgbuild: Wrote package to XXX.pkg
```

署名ができたか確認。

```
$ pkgutil --check-signature XXX.pkg 
Package "XXX.pkg":
   Status: signed by a developer certificate issued by Apple (Development)
   Certificate Chain:
    1. Developer ID Installer: Your Name (XXXXXXXXXX)
       Expires: 2022-XX-XX 00:27:46 +0000
       SHA256 Fingerprint:
           XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX 
           XX XX XX XX XX XX XX XX XX XX
       ------------------------------------------------------------------------
    2. Apple Worldwide Developer Relations Certification Authority
       Expires: 2030-XX-XX 00:00:00 +0000
       SHA256 Fingerprint:
           XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX 
           XX XX XX XX XX XX XX XX XX XX
       ------------------------------------------------------------------------
    3. Apple Root CA
       Expires: 2035-XX-XX 21:40:36 +0000
       SHA256 Fingerprint:
           XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX 
           XX XX XX XX XX XX XX XX XX XX
```

## Pkgを作成する ##
distribution.xmlというファイルを下記の内容で作成する。

```distribution.xml
<?xml version="1.0" encoding="utf-8"?>
<installer-gui-script minSpecVersion="1">
    <title>My Product Installer</title>
    <pkg-ref id="your.bundle.id"/>
    <options customize="never" require-scripts="false" hostArchitectures="x86_64,arm64"/>
    <choices-outline>
        <line choice="default">
            <line choice="your.bundle.id"/>
        </line>
    </choices-outline>
    <choice id="default"/>
    <choice id="your.bundle.id" visible="false">
        <pkg-ref id="your.bundle.id"/>
    </choice>
    <pkg-ref id="your.bundle.id" version="X.X.X" onConclusion="none">XXXFlat.pkg</pkg-ref>
</installer-gui-script>
```

productbuildを使ってpkgを作成。

```
$ productbuild --distribution distribution.xml --package-path "XXXFlat.pkg" --sign "Developer ID Installer: Your Name (XXXXXXXXXX)" "XXX.pkg"

productbuild: Signing product with identity "Developer ID Installer: Your Name (XXXXXXXXXX)" from keychain /Users/xxx/Library/Keychains/login.keychain-db
productbuild: Adding certificate "Apple Worldwide Developer Relations Certification Authority"
productbuild: Adding certificate "Apple Root CA"
productbuild: Wrote product to XXX.pkg
```

## ディスクイメージ (dmg) を作成する ##
できあがったpkgを適当なフォルダに入れて、hdiutilを使ってディスクイメージを作成。
```
$ hdiutil create -srcfolder "folder for dmg" -fs HFS+ -format UDZO -volname "dmg volume name" XXX.dmg
```

## Notarization ##
まずApple IDとかパスワードとか、credentialを登録しておく。これは一度やれば次回以降は必要ない。
```
$ xcrun notarytool store-credentials
```
対話形式で色々聞かれるので答えていく。
* Profile name - 任意のプロファイル名
* Path to App Store Connect API private key - 何も入れずenter
* Developer Apple ID - Apple ID
* Password for (AppleID) - Appパスワード
* Developer Team ID - Team ID
としていくと、最後に
```
To use them, specify --keychain-profile "XXXXXXXX"
```
と出る（XXXXXXXXは最初に入力した任意のプロファイル名）。このプロファイル名はしっかり保存しておく。そしたら、
```
$ xcrun notarytool submit XXX.dmg --keychain-profile "XXXXXXXX" --wait
```
でNotarizationできる。エラーが出たらStatusがInvalidになるので、ログをダウンロードして原因を調べる。

## エラーの原因を調べる ##
下記のコマンドでログを取って調べる。
```
$ xcrun notarytool log XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX --keychain-profile "XXXXXXXX" error.log
```
* XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX - 表示されたUUIDを指定
* XXXXXXXX - Keychain Profile名