TravisCIでElectronアプリを自動でビルド、署名、公証してGitHubにリリースする手順メモ

## これはなに?
- GitHubとTravisCIを使って、Electronアプリを自動でビルドするための設定手順です。 
- Linux、Windows、macOS向けのビルドに対応。

## 何をしたいの?
- Electronのビルド〜署名〜公証を手動で実行するのが面倒なので、TravisCIで自動化させたい。
- ついでにGitHubのリリースタグにビルドの成果物(.exeとか)を自動で添付してほしい。

# TravisCIが自動でしてくれること
- Electronアプリのビルド (.exe, .AppImage, .dmg を作成する)
- アプリの署名 (MacOSのみ)
- アプリの公証。Notarize。Gatekeeperで弾かれるのを防ぐため (MacOSのみ)
- ビルドした成果物をGitHubのドラフトタグのAssetsに添付する。

## 使用するライブラリ
- [electron-builder](https://github.com/electron-userland/electron-builder) - Electronをビルドするライブラリ。
- [electron-notarize](https://github.com/electron/electron-notarize) - アプリの公証をするためのライブラリ。


## 以下、手順

### 1. 必要なパッケージをインストール

electron-builderとelectron-notarizeをインストールします。

```
npm install electron-builder electron-notarize
```

### 2. electron-builderの設定をpackage.jsonに追加

`package.json`の`build`にelectron-builderの設定を書いていきます。必要最低限の設定を下記に記載しています。自由にカスタマイズしてください。

```json:package.json
{
  "name": "example",
  "version": "1.0.0",
  "author": "example <example@example.com>",
  "description": "Example",
  "license": "MIT",
  ...
  "build": {
    "productName": "Example",  # アプリの名前
    "appId": "app.example",    # アプリの識別子
    "afterSign": "./notarize", # 署名後にnotarizeを走らせる。後述。
    "publish": {
      "provider": "github",    
      "releaseType": "draft"   # ドラフト状態のタグがあれば、Assetsにビルド成果物を添付する
    }
  },
  "dependencies": {
   ...
  },
  "devDependencies": {
   ...
  }
}

```

### 3. .travis.ymlにビルドの設定を追加する

.travis.ymlにビルドの設定を記述します。
[electron-builderのdocs](https://www.electron.build/multi-platform-build#sample-travisyml-to-build-electron-app-for-macos-linux-and-windows)を参考に作成しました。


```yaml:.travis.yml
cache:
  directories:
    - node_modules
    - $HOME/.cache/electron
    - $HOME/.cache/electron-builder

jobs:
　　 # MacOS用にビルドするJob
    - stage: build
      os: osx
      osx_image: xcode11
      language: node_js
      node_js: 10
      # masterにpushした場合か、プルリクエストの場合のみビルドする。
      # ただし、成果物 (.exe, .AppImage, .dmg) をAssetsに添付するのはmasterにpushした場合のみ。
      # プルリクエストの場合はelectron-builderが自動で添付をスキップしてくれる。
      if: (branch = master AND type = push) OR type = pull_request
      script: npm run build -- --mac

　　 # Win用、Linux用にビルドするJob。wineコンテナ上で実行される。
    -
      os: linux
      services: docker
      language: generic
      node_js: 10
      before_cache:
        - rm -rf $HOME/.cache/electron-builder/wine
      if: (branch = master AND type = push) OR type = pull_request
      script:
        - |
          docker run --rm \
            -e ELECTRON_CACHE=$HOME/.cache/electron \
            -e ELECTRON_BUILDER_CACHE=$HOME/.cache/electron-builder \
            -e CI=$CI \
            -e GH_TOKEN=$GH_TOKEN \
            -e TRAVIS_PULL_REQUEST=$TRAVIS_PULL_REQUEST \
            -v ${PWD}:/project \
            -v ~/.cache/electron:/root/.cache/electron \
            -v ~/.cache/electron-builder:/root/.cache/electron-builder \
            electronuserland/builder:wine \
            /bin/bash -c "\
              npm install && \
              npm run build -- --linux --win
            "
```


### 4. 公証するスクリプトを作成

MacOS用のために公証するスクリプトを作成します。公証しないと、Gatekeeperにブロックされてアプリが開けません。公証については、[Appに署名してGatekeeperで保護する](https://developer.apple.com/jp/developer-id/)を参照。
このスクリプトはpackage.jsonのafterSignに設定されている通り、署名後に自動で呼び出されます。

```javascript:notarize.js
const path = require('path')
const { notarize } = require('electron-notarize')

// TravisCIの環境変数 (process.env) から取得
const appleId = process.env.APPLE_ID
const appleIdPassword = process.env.APPLE_PASSWORD

// .appのパスを設定
const appPath = path.resolve(__dirname, '../dist/mac/Example.app')

// package.jsonからappIdを取得
const configPath = path.resolve(__dirname, '../package.json')
const config = require(configPath)
const appBundleId = config.build.appId

exports.default = async () => {
  // appleId, appleIdPasswordが取得できない場合はスキップ
  // プルリクエストの場合も公証をスキップ
  if (!appleId || !appleIdPassword || process.env.TRAVIS_PULL_REQUEST !== 'false') {
    console.warn('notarize skipped');
    return;
  }

  // notarizeする
  await notarize({
    appBundleId,
    appPath,
    appleId,
    appleIdPassword,
  })
}
```

### 5. TravisCIのENVにビルドに必要な機密情報を設定していく

ここからは、ビルド、署名、公証に必要な機密情報をTravisCIのENVに設定していきます。
TravisCIのENVに設定する方法は、[Environment Variables](https://docs.travis-ci.com/user/environment-variables/#defining-variables-in-repository-settings) を参照してください。全部で5項目あります。

<img width="1025" alt="スクリーンショット 2019-11-24 16.32.51.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407015/7f2678b2-6c04-189a-3ff8-03867a07ae23.png">

#### GitHubのアクセストークンを設定

ビルド成果物をリリースタグのAssetsに添付するために必要になります。
[Personal access tokens](https://github.com/settings/tokens) からトークンを取得します。scopeは`repo`のみで可。

| name       |       value |
|:-----------------|:------------------|:------------------|
| GH_TOKEN             |              発行したアクセストークン |


#### Apple IDとApp用パスワードを設定

公証するのに必要になります。

1. [Apple IDのアカウント設定](https://appleid.apple.com/account/manage)にアクセスします。
2. `セキュリティ > App用パスワード` からパスワードを生成します。ラベルは適当で可。

| name       |       value |
|:-----------------|:------------------|:------------------|
| APPLE_ID             |              Apple ID (ログイン時のメールアドレス) |
| APPLE_PASSWORD           |            発行したApp用パスワード |


#### Developer ID証明書を発行

署名に使う証明書を発行します。

キーチェーンを開いて、Developer ID証明書を発行するための証明書署名リクエスト（CSR）を作成します。参考: [証明書署名リクエストを作成する](https://help.apple.com/developer-account/#/devbfa00fef7)
<img width="328" alt="スクリーンショット 2019-11-19 23.30.23.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407015/951508a5-15a7-2e1a-e7df-ebdd12f85220.png">


Apple Developerアカウントの「[Certificates, Identifiers & Profiles](https://developer.apple.com/account/resources/certificates/list)」にアクセスします。「+」ボタンをクリック。
<img width="1268" alt="スクリーンショット 2019-11-24 16.04.24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407015/e85c667e-c680-13e4-2af5-78f0c676d85d.png">

Developer ID Applicationを選択
<img width="1268" alt="スクリーンショット 2019-11-24 16.06.59.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407015/12be3aba-599a-48a0-c3ae-1fee84dc96db.png">

「Choose File」をクリックして、作成したCSR (.certSigningRequest) を選択。

<img width="1268" alt="スクリーンショット 2019-11-24 16.10.10.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407015/41e9fec2-c1f8-d79e-a459-1c44f96b55e2.png">

証明書をダウンロードします。
![スクリーンショット 2019-11-19 23.31.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407015/325a2b6c-d0a5-e44b-f940-f21ebcd62e0d.png)

キーチェーンからダウンロードした証明書の秘密鍵を書き出します。
<img width="329" alt="スクリーンショット 2019-11-19 23.33.12.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407015/8b6b53a3-e65e-860a-c6cf-562ea81d4c0a.png">

パスワードを設定するダイアログが表示されるので設定します。
<img width="329" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407015/9d531299-65bf-322b-750c-4db3f25335d7.png">


base64コマンドなどで書き出したp12ファイルをBase64形式に変換します。

  ```
  $ base64 ~/Downloads/example.p12 
  ```

Base64形式に変換した証明書を「CSC_LINK」として設定します。また、証明書を書き出す際に設定したパスワードを「CSC_KEY_PASSWORD」としてTravisCIのENV設定します。

| name       |       value |
|:-----------------|:------------------|:------------------|
| CSC_LINK             |              p12をbase64に変換したもの |
| CSC_KEY_PASSWORD             |             証明書を書き出す際に設定したパスワード |

以上で、設定は完了です！

## リリースフローを実際にやってみる

実際にリリースの流れを試してみましょう。

まずは、GitHubのリポジトリからリリースタグをドラフト状態で作成します。
今回は新しいバージョンを「v1.1.0」としました。
<img width="1025" alt="スクリーンショット 2019-11-24 17.05.10.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407015/18bcd15a-1456-be11-bd8f-8a4724c48759.png">

まだ添付されていない状態です。
<img width="1025" alt="スクリーンショット 2019-11-24 17.07.29.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407015/d8be556c-74ee-a3c5-ad63-fcdff2a0a684.png">

では、`package.json`の`version`を「1.1.0」に変更します。先程作成したタグのバージョンと合わせる必要があります。
<img width="382" alt="スクリーンショット 2019-11-24 17.08.09.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407015/115c171b-e990-5024-f21b-a1825f65997d.png">

masterにpushします。

```sh
$ git add package.json
$ git commit -m "release v1.1.0"
$ git push origin master
```

masterのpushをトリガーにして、TravisCIのビルドが走ります。
<img width="539" alt="スクリーンショット 2019-11-24 17.12.49.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407015/ecd2b471-a226-b4c0-566c-113819c62d47.png">

TravisCIのビルドが完了すると、ドラフトタグのAssetsにビルド成果物が自動で添付されています。
<img width="1039" alt="スクリーンショット 2019-11-24 17.17.23.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407015/8dd9aa24-adce-c7fd-ebb9-a2f2368cfa90.png">

添付されたものを確認して、問題なければタグのドラフト状態を解除して公開しましょう！
以上がリリースフローになります。




# サンプル
サンプルのリポジトリもGitHubに公開していますのでご参考ください。
https://github.com/ktmouk/electron-travis-example

# 参考
- https://uechi.io/blog/sign-and-notarize-electron-app
- https://qiita.com/nanairo24/items/73356574b0dc65c0e617