Bitrise と DeployGate で React Native アプリの評価環境を構築する

リリース前につくったもののフィードバックがほしい。だけどインストールやなんかでの手作業を避けたい。というわけで自動化のお時間です。図にするとこういうことがしたい。
![hexagram.png](https://qiita-image-store.s3.amazonaws.com/0/27516/08467565-b463-cba5-4468-77426e10245d.png)

右上から順に次のサービスを使う。

| 用途                      | サービス名    | URL                       |
|---------------------------|---------------|---------------------------|
| ソースバージョン管理      | GitHub        | https://github.com/       |
| CI                        | CircleCI      | https://circleci.com/     |
| スマホアプリ向けビルド    | Bitrise       | https://www.bitrise.io/   |
| アプリ配布                | DeployGate    | https://deploygate.com/   |
| チャット                  | Slack         | https://slack.com/        |

Bitrise でもテストできるので一見 CircleCI はいらないように見えるんだけど、 Bitrise は push / pull request でしか trigger できないので細かい制御、例えばリリース用の tag を付加したときだけ動かしたい場合にやっぱりいる。


Bitrise.io
----------

スマホアプリ向け CI サービス。スマホアプリに特化しているだけあってかなり楽に iOS / Android のビルド設定ができる。具体的には次がうれしい。

- Provisioning Profile や keystore などをアップロードできるのでセキュリティ的に安心
    - ↑を github repo に commit しなくてよい
    - Bitrise を信用する必要はある
- メモリーが足りなかったりビルド時間が長すぎて打ち切られたりがない
    - 少なくともいまのところこれで落ちたことはない
- UI でぽちぽちやれば設定できる
    - 最終的に bitrise.yml という設定ファイルに落ちるけど意識せずに UI だけで使える
    - もちろん Infrastructure as Code として bitrise.yml を管理してもいい
- CLI ツールがある
    - http://devcenter.bitrise.io/docs/bitrise-command-line-interface-how-to-guide
    - chat bot から叩いたりといったことができる
- 自前で step を定義できる
    - bitrise は step と呼ばれる処理をひとまとまりにして、連続的な step を組むことでビルドをする
    - step を作成して公式に登録できる
        - http://devcenter.bitrise.io/docs/bitrise-commandline-interface-sharing
    - 公式の step も github からソースが見られるのでトラブルシュートしやすい
- 無料でも実用的
    - https://www.bitrise.io/me/profile/pricing
    - 無料でも普通に使える
        - 2 team members
        - 10 min / build
        - 200 builds / month
        - 1 concurrency
        - iOS, Android, Xamarin support*
        - Access 60+ integrations
        - Slack & Email tech support
    - plan は $50 / month のひとつだけで、上記の制限が緩和される


前提
----

ビルド対象のプロジェクトは React Native ベースなので次の構造になってる。 &lt;ProjectName> は `react-native init <ProjectName>` したときに指定したプロジェクトの名前。

```
/
    android/                        Android native 用ディレクトリー
        build.gradle                apk ビルド用 gradle ファイル
        gradlew                     apk ビルドに使う gradle wrapper
    ios/                            iOS native 用ディレクトリー
        <ProjectName>.xcodeproj/    Xcode プロジェクト
    test/
        spec/                       仕様系ユニットテスト
```

以降で手順を書く。 `react-native init` 直後のものだけど次のコードをサンプルに設定する。

https://github.com/januswel/rn-pomodoro/

テストについてはまた別途書く。


iOS
---

### Bitrise 設定

#### アプリ設定のセットアップ

"ADD NEW APP" からアプリを登録する。次はミニマムの選択肢。

| 設定項目                      | 選択                  |
|-------------------------------|-----------------------|
| Setup repository access       | AUTOMATIC             |
| Do you need to use an additional private repository?  | No, auto-add SSH key  |
| Validation Setup              | master                |
| Project Build Configuration   | iOS                   |
| Webhook setup                 | Skip the Webhook registration |

"Webhook setup" は github から直接たたくわけではないので skip している。もちろん push ごとにビルドしたい場合はここで "Register a Webhook for me!" を選ぶ。

#### React Native 用設定

ビルド用のコンテナーに npm が入っているので `npm install` するだけ。

1. アプリ設定の "Workflow"
2. "MANAGE WORKFLOWS"
3. "Git Clone Repository" と "Certificate and profile installer" の間にある "Do anything with Script step"
4. "npm install" に "rename"
5. "Script content" の `echo "Hello World!"` 部分を `npm install` に変更


### Provisioning Profile 設定

なんかあったときに作り直せたほうがいいので専用の App ID で Provisioning Profile を作る。本配布するわけではないので Development で。次はイチから作る手順。

#### 証明書作成

1. Xcode のメニュー "Xcode"
2. "Preferences"
3. "Accounts"
4. "Team" で自分を選ぶ
5. "View Details"
6. "iOS Development" の "Create" を押す

![certificate.png](https://qiita-image-store.s3.amazonaws.com/0/27516/684425e9-2457-e47d-b184-bca5fea4103f.png)

Apple Developer Center の Certificates に作ったものがあることを確認。

![certificate02.png](https://qiita-image-store.s3.amazonaws.com/0/27516/895ed555-8f8c-1575-f3a2-70e39b21cdbc.png)

#### App ID 作成

1. Apple Developmer Program のトップから "Certificates, IDs & Profiles"
2. "Identifires" の下の "App IDs"
3. 右上の "+" から作る

| 設定項目              | 選択・値                          |
|-----------------------|-----------------------------------|
| App ID Description    | アプリ説明をいれる                |
| App ID Suffix         | Explicit App ID                   |
| Bundle ID             | Xcode での設定と同じ一意な識別子、よく URL を逆さにしたものが使われる  |
| App Services          | アプリで使うサービスを指定する    |

App ID は自分のドメイン名をドットごとに逆さにして、最後にアプリ名をくっつけるのが慣習になってる。ドメインを持ってない場合はテケトーに指定で。ここが他人とかぶってても Prefix で一意に識別されるので問題ない。

作ったら次のようなものが一覧に表示される。 Prefix は開発者やチームごとに違うものが付与される。

![app-id.png](https://qiita-image-store.s3.amazonaws.com/0/27516/7e3fc3b6-4f45-5cc8-2c64-3bd3b7a9a966.png)

#### Provisioning Profile 作成

1. Apple Developmer Program のトップから "Certificates, IDs & Profiles"
2. "Provisioning Profiles" の下の "Development"
3. 右上の "+" から作る

| 設定項目                                          | 選択                  |
|---------------------------------------------------|-----------------------|
| What type of provisioning profile do you need?    | iOS App Development   |
| App ID                                            | さっきつくったやつ    |
| Select Certificates                               | さっきつくったやつ    |
| Select Devices                                    | 配布したい実機を選択  |
| Profile Name                                      | テケトーに            |

あとで使うので作ったら同時にダウンロードしておく。

![provisioning-profile.png](https://qiita-image-store.s3.amazonaws.com/0/27516/a4bcc29f-d39d-f863-5577-54f9357f4d4c.png)

配布したい実機が増えた場合、 "Edit" して "Devices" の欄で追加する。再ダウンロードと後述の Bitrise へのアップロードも忘れずに。

#### Xcode 設定

1. Xcode のメニュー "Xcode"
2. "Preferences"
3. "Accounts"
4. "Team" で自分を選ぶ
5. "View Details"
6. "Provisioning Profiles" の該当のものを "Download"

![provisioning-profile02.png](https://qiita-image-store.s3.amazonaws.com/0/27516/c4e99180-5c9c-799a-fb6c-b0309101156e.png)

アプリにも設定する。

1. Xcode 左上のプロジェクト名
2. "Build Settings"
3. "Code Sigining"
4. "Code Sigining Identity" を iPhone Developer に変更

![xcode.png](https://qiita-image-store.s3.amazonaws.com/0/27516/d02e75b3-682c-b85d-5208-4c16ee902d2a.png)

Bundle Identifier も設定。

1. Xcode 左上のプロジェクト名
2. "General"
3. "Bundle Identifier" にうえでつくった App ID を指定


#### 証明書エクスポート

1. KeyChain の "My Certificates"
2. "iPhone Developer: 名前 (識別子)" を選択
3. メニューの "File" -> "Export Items"
4. "Save"
5. パスワードを入れる
    - 外部に預けるわけなので頑強なものを指定する

![keychain.png](https://qiita-image-store.s3.amazonaws.com/0/27516/f5252df8-e6dd-a2b2-ba3b-8c6426fe4cb4.png)

#### Bitrise 設定

1. アプリ設定の "Workflow"
2. "MANAGE WORKFLOWS"
3. 左の "Code signing & Files"
4. Provisioning Profile にさっきつくってダウンロードした Provisioning Profile をアップロード
5. Code Signing Identity にさっきエクスポートした証明書をアップロード
    - Password にエクスポート時に指定したパスワードをいれる

ここまでで一回ビルドを走らせて、できた ipa ファイルを落としておく。

![get-ipa.png](https://qiita-image-store.s3.amazonaws.com/0/27516/283e83ad-a23e-78a3-3188-d7477f12fa23.png)


### DeployGate へのアップロード設定

#### 配布ページを作る

配布ページがないとアップロードするたびにダッシュボードに項目が増えていくので配布ページを作る。とはいっても関係者だけにしか配りたくないので合言葉で制限をかける。

1. DeployGate の「ダッシュボード」からさっき落とした ipa ファイルをアップロード
2. 右上の「共有用のリンクを追加」
3. 左の「配布ページの設定」
4. 「合言葉が必要」を選択
5. 合言葉を入力
6. 「配布ページ設定の更新」

改めて配布ページに移動して URL の末尾の数字とアルファベットの列をひかえる。これが distribution key になる。

#### API key をひかえる

1. DeployGate の自分のユーザー名 -> 「アカウント設定」
2. 一番下の API key をひかえておく

#### Bitrise 設定

1. アプリ設定の "Workflow"
2. "MANAGE WORKFLOWS"
3. "Deploy to Bitrise.io" を削除する
4. "Xcode Archive" の下にある "+"
5. "Script" を "Add to Workflow"
6. "Upload to DeployGate" に "rename"
7. "Script content" に次を指定

```sh
curl \
    -F "file=@$BITRISE_IPA_PATH" \
    -F "token=$DEPLOYGATE_API_KEY" \
    -F "message=$BITRISE_APP_TITLE $BITRISE_BUILD_NUMBER" \
    -F "distribution_key=$DEPLOYGATE_DISTRIBUTION_KEY" \
    -F "release_note=$GIT_CLONE_COMMIT_MESSAGE_SUBJECT" \
    https://deploygate.com/api/users/$DEPLOYGATE_USERNAME/apps
```

"message" と "release_note" はテケトーに。ここでは Bitrise 標準で使えるものを指定してる。その他の設定は次。

1. アプリ設定の "Workflow"
2. "MANAGE WORKFLOWS"
3. 左の "Secret Env Vars"
4. 次のように設定をいれる

| 変数名                        | 設定内容                                      |
|-------------------------------|-----------------------------------------------|
| DEPLOYGATE_USERNAME           | DeployGate のユーザー名                       |
| DEPLOYGATE_API_KEY            | うえでひかえた DeployGate の API key          |
| DEPLOYGATE_DISTRIBUTION_KEY   | うえでひかえた配布ページの distribution key   |


### Slack への通知設定

まだ Slack 側で対応していないようなので Bitrise 側の公式 step を使う。

#### Slack 設定

1. http://<team>.slack.com/apps
2. "Incoming WebHooks" を追加
3. "Install"
4. チャンネルを選択するか、新しいチャンネルをつくる
5. "Add Incoming WebHooks Integration"
6. "Webhook URL" をひかえる

#### Bitrise 設定

1. アプリ設定の "Workflow"
2. "MANAGE WORKFLOWS"
3. "Upload to DeployGate" の下にある "+"
4. "Send a Slack message" を "Add to Workflow"
5. "Should this step run even if a previous step failed?" を "Yes" に
6. "Slack Webhook URL" にうえでつくった "Webhook URL" を入力
7. "Target Slack channel, group or username" にうえで指定したチャンネルを指定
8. "The message you want to send" はテケトーに埋める
    - ここが空白だと Slack 側からエラーが返ってくる
    - `$BITRISE_APP_TITLE $BITRISE_BUILD_NUMBER is available!!` とかしとけばいいんじゃないでしょうか
    - DeployGate のアプリの管理ページを通知するとか
9. "The message you want to send - if the build failed" も同様に
    - `$BITRISE_BUILD_URL` を載せとくと一発で開けて便利
10. その他は適当に

他には、ぱっと見でどういうステータスなのかわかりやすいように、 Slack 側に Bitrise のアイコンを加工したものを custom emoji として上げて、それを指定してたりする。

![bitrise-success.png](https://qiita-image-store.s3.amazonaws.com/0/27516/619fda99-288d-e590-fd70-be9ec0ed2f42.png)
![bitrise-failure.png](https://qiita-image-store.s3.amazonaws.com/0/27516/79b1b665-22b6-f8e6-5cae-cc46db4cc9c8.png)

ここまででひととおりの設定は完了。 "Start Build" してうまく DeployGate にアップロードされるかどうか、 Slack に通知がくるかどうかを確かめる。


Android
-------

流れは iOS と同じなので異なる部分だけ列挙する。

### Bitrise 設定

#### アプリ設定のセットアップ

最後のプラットフォーム指定だけ違う。

1. "Project build configuration" で Android を選ぶ
2. "Select Gradle task to run" で assembleRelease を選ぶ

### npm インストール

Android ビルド用のコンテナーは Node.js が入ってない。次を参考にインストールする。

http://devcenter.bitrise.io/docs/building-react-native-projects-on-bitrise

1. アプリ設定の "Workflow"
2. "MANAGE WORKFLOWS"
3. "Git Clone Repository" と "Certificate and profile installer" の間にある "Do anything with Script step"
4. "npm install" に "rename"
5. "Script content" の `echo "Hello World!"` 部分を次に変更

```sh
curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -
apt-get install -y nodejs
npm install
```

### 署名設定

#### keystore 作成

署名用の keystore を作る。

```sh
keytool -genkey -v -keystore staging.keystore -alias pomodoro -keyalg RSA -keysize 2048 -validity 10000
```

名前とか組織名とか所在地とか聞かれるので正直に。パスワードは keystore, alias どっちも同じものでいいけどやはり外部に預けるので頑強なものを指定すること。

#### Bitrise 設定

まず keystore の設定。

1. アプリ設定の "Workflow"
2. "MANAGE WORKFLOWS"
3. 左の "Code signing & Files"
4. "Add another File"
5. "Select file type" を "Android Keystore" に
6. "Keystore password", "Keystore alias", "Private key password" に keystore 作成時に指定したものを入力
7. "Save metadata"

つぎ、署名用の設定。

1. アプリ設定の "Workflow"
2. "MANAGE WORKFLOWS"
3. "Gradle Runner" の下の "+"
4. "Sign APK" を "Add to Workflows"

### DeployGate へのアップロード設定

Script step に指定するコードがちょっと違う。 "file" に指定する環境変数を `$BITRISE_SIGNED_APK_PATH` にするだけ。

```sh
curl \
    -F "file=@$BITRISE_SIGNED_APK_PATH" \
    -F "token=$DEPLOYGATE_API_KEY" \
    -F "message=$BITRISE_APP_TITLE $BITRISE_BUILD_NUMBER" \
    -F "distribution_key=$DEPLOYGATE_DISTRIBUTION_KEY" \
    -F "release_note=$GIT_CLONE_COMMIT_MESSAGE_SUBJECT" \
    https://deploygate.com/api/users/$DEPLOYGATE_USERNAME/apps
```


Github に特定のタグが push されたらビルドするための設定
-------------------------------------------------------

### CircleCI

対象の repo を追加する。 xcodeproj が含まれているので CircleCI は勝手に Mac 用のビルド設定にするけど、ここでビルドはしないので "Project Settings" -> "Build Environment" -> "Build OS X Project" を Off にする。

![circleci.png](https://qiita-image-store.s3.amazonaws.com/0/27516/a6e4a11b-1dec-d756-107c-e4543f48edbe.png)

あとは circle.yml 内で指定する。

```yml
---
machine:
  node:
    version: 6.3.0
deployment:
  staging:
    tag: /v(0|[1-9][0-9]*)(\.(0|[1-9][0-9]*)){2}/
    commands:
      - bin/bitrise.sh
```

deployment セクションで指定している bitrise.sh の中身は次になる。 Bitrise の "Start build" -> "Advanced" の一番下にあるコマンドのうち、見えたらまずい部分を環境変数にくくりだしているだけ。

```sh
#!/bin/sh

curl https://www.bitrise.io/app/${BITRISE_APP_SLUG_IOS}/build/start.json --data '{"hook_info":{"type":"bitrise","api_token":"'${BITRISE_API_TOKEN_IOS}'"},"build_params":{"branch":"master"}}'
curl https://www.bitrise.io/app/${BITRISE_APP_SLUG_ANDROID}/build/start.json --data '{"hook_info":{"type":"bitrise","api_token":"'${BITRISE_API_TOKEN_ANDROID}'"},"build_params":{"branch":"master"}}'
```

使ってる環境変数は次のように CircleCI の "Project Settings" -> "Environment Variables" から設定する。

| 環境変数名            | 説明                                                      |
|-----------------------|-----------------------------------------------------------|
| BITRISE_APP_SLUG_IOS  | iOS アプリの Bitrise 設定を指定する識別子                 |
| BITRISE_API_TOKEN_IOS | iOS アプリの Bitrise 設定で発行された API トークン        |
| BITRISE_APP_SLUG_ANDROID  | Android アプリの Bitrise 設定を指定する識別子             |
| BITRISE_API_TOKEN_ANDROID | Android アプリの Bitrise 設定で発行された API トークン    |


これでおわり。

bitrise.yml
-----------

設定した内容が yaml として次から参照できるので、手元にバックアップとして残しておくといい。

1. アプリ設定の "Workflow"
2. "MANAGE WORKFLOWS"
3. 左の "bitrise.yml"

みんなで共有できるように git repo に commit してしまってよいかも。今回の設定は次に commit してある。

https://github.com/januswel/rn-pomodoro/blob/master/resources/bitrise-ios.yml
https://github.com/januswel/rn-pomodoro/blob/master/resources/bitrise-android.yml

残り
---

うまくいくとこんな感じになるよ。

![slack.png](https://qiita-image-store.s3.amazonaws.com/0/27516/e43d3a38-67a3-2896-766d-0b5b06ebb377.png)

テストの書き方とか、 production, staging, development の切り分けとか、もっと下回りで整備することがあるんだけどそれはまた別で記事を書く !!