Bitriseで本をビルドする #bitrise #ReView #iOSテスト全書

[Bitrise Advent Calendar](https://qiita.com/advent-calendar/2019/bitrise) 8日目の[かっくん(@fromkk)](https://twitter.com/fromkk)です。
今年の頭ぐらいから始動した[iOSテスト全書](https://peaks.cc/iOS_testing)でレビューを担当しました。
ようやく発送も始まったので注文済みの人は楽しみに待っていてください！

僕はレビューする際は実際にPDFの形になってる方が有難かったので、読む時は都度都度Macでビルドして本をiPhoneやiPadに転送して読んで、気になる所は印を付けて後でフィードバックする様な方法を取っていました。
ある時気付いたんですよね。

**このそうそうたるメンバーが揃っていながらCI/CD出来てないとはどういう事だ！？**

ただ、皆には本を書く事に集中して欲しかったのと、僕が個人で契約しているBitriseアカウントがあったので、最初は個人的な遊びとしてBitrise上でビルドする事を試みました。

## 前提条件

- macOS Catalina(10.15.1（19B2106）)

さすがに[iOSテスト全書](https://peaks.cc/iOS_testing)の環境をそのまま紹介するわけにはいかないので[TechBooster/ReVIEW-Template](https://github.com/TechBooster/ReVIEW-Template)をcloneして自分用にカスタマイズして利用します。
[fromkk/Bitrise_Docker_Review_Sample](https://github.com/fromkk/Bitrise_Docker_Review_Sample)にサンプルを配置しておきましたので興味のある方は見ていただければと思います。

## ローカル環境での動作確認

まずはDockerがインストールされている前提で話を進めます。
[ReVIEW-Template](https://github.com/TechBooster/ReVIEW-Template)を参考にしながら `docker pull vvakame/review:4.0` を実行します。
そして `./build-in-docker.sh` を実行してみたところ

```sh
% ./build-in-docker.sh
+ rm -rf node_modules
+ npm install --unsafe-perm

> review-template@0.0.2 postinstall /book
> bundle install

Traceback (most recent call last):
	2: from /usr/local/bin/bundle:23:in `<main>'
	1: from /usr/lib/ruby/2.5.0/rubygems.rb:303:in `activate_bin_path'
/usr/lib/ruby/2.5.0/rubygems.rb:284:in `find_spec_for_exe': Could not find 'bundler' (1.17.2) required by your /book/Gemfile.lock. (Gem::GemNotFoundException)
To update to the latest version installed on your system, run `bundle update --bundler`.
To install the missing version, run `gem install bundler:1.17.2`
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! review-template@0.0.2 postinstall: `bundle install`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the review-template@0.0.2 postinstall script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /root/.npm/_logs/2019-12-07T03_22_42_956Z-debug.log
```

とエラーが出てしまったので `build-in-docker.sh` ファイルを

```diff
- docker run -t --rm -v $(pwd):/book vvakame/review:4.0 /bin/bash -ci "cd /book && ./setup.sh && REVIEW_CONFIG_FILE=$REVIEW_CONFIG_FILE npm run pdf"
+ docker run -t --rm -v $(pwd):/book vvakame/review:4.0 /bin/bash -ci "cd /book && gem install bundler:1.17.2 && ./setup.sh && REVIEW_CONFIG_FILE=$REVIEW_CONFIG_FILE npm run pdf"
```

と書き換えて再実行。

```sh
% ./build-in-docker.sh 
Fetching: bundler-1.17.2.gem (100%)
Successfully installed bundler-1.17.2

.
.
.

INFO review-pdfmaker: uplatex -interaction=nonstopmode -file-line-error -halt-on-error __REVIEW_BOOK__.tex
INFO review-pdfmaker: dvipdfmx -d 5 -z 9 __REVIEW_BOOK__.dvi

Done.
```

これで `./articles` フォルダの配下にPDFファイルが生成されているはずです。(`config.yml` に設定してある `bookname` がファイル名になります)
これで最低限の確認が完了です。続いてBitriseの設定に移ります。

# Bitriseでビルド

普段Bitriseを利用する時は大体iOSアプリやMacのCLIアプリをビルドする為、何も考えずにMacのマシンを選んでいました。
ただ、re:viewをビルドする為にDockerを利用したかったのですが、BitriseのMacマシンにはDockerが入っていませんでした。
調べてみた所BitriseのLinuxマシンにはDockerが入っている事が分かり、先に進める事が出来ました。

## セットアップ

BitriseはiPhoneやAndroidアプリをビルドする事に特化しており、セットアップも少しだけ手こずりました。

`Add New App` -> `Add New App on web UI` をクリック

<img width="279" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846/bbbc032e-3d5b-bdb7-98ca-dabdc7c78b0a.png">

公開範囲を設定

<img width="412" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846/ce26ef16-b651-155c-aeed-ab087d37221b.png">

リポジトリを選択

<img width="611" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846/8fed8e54-6363-1917-6c41-32e11cf50f65.png">

ブランチを選択

<img width="624" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846/930047ba-77c5-1003-142f-9b5d77fed464.png">

バリデートが実行され

<img width="1664" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846/35b27caf-75b2-09fd-4231-0fefab297b0b.png">

**見事に失敗します！**

<img width="437" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846/532e7992-076b-69b8-6870-3af850391229.png">

iOS/Android向けのビルドじゃないので当たり前ですね。
`Restart scanning without validation` を選択して続けます。

<img width="676" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846/d8477e21-4a86-b6dc-65b5-4cca7029009e.png">

無事バリデーションが完了します

<img width="406" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846/331873f6-7d45-acda-5bc1-c559c7bed672.png">

`OTHER/MANUAL` から `Android & Docker, on Ubuntu 16.04` を選択して `I'm ready` をクリックします。

<img width="674" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846/5cc93ca0-367b-e80a-47a4-c54b880e8669.png">

アイコンは今は不要なので `Skip` してWebhookを設定します。

<img width="668" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846/eeba40fb-9f45-1869-3d40-65c975e3f62f.png">

ようやく初期設定が完了です。 `Stack` を手動で設定するところが少しややこしかったですね。

<img width="775" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846/e9d85803-9b0b-e8a4-5949-2725dc5505ba.png">

とりあえずビルドを実行しますがまだ本は出来ないので Workflowを編集します。

## ビルド

`Workflow Editor` で `Primary` を選択すると `Do anything with Script step` というステップが最初から用意されているのでここを次の様に編集します。

<img width="926" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846/89677f94-7354-aeaf-3472-c63ad0fc27f0.png">


コードだけ抜き出すとこんな感じ

```sh
#!/usr/bin/env bash
# fail if any commands fails
set -e
# debug log
set -x

docker pull vvakame/review:4.0
./build-in-docker.sh

file_name=$(cat articles/config.yml | grep "bookname: " | sed -e s/'bookname: '/''/g)

now="$(TZ=Asia/Tokyo date +%Y%m%d_%H%M%S)"
pdf_path="$(pwd)/articles/${file_name}.pdf"

envman add --key PDF_PATH --value $pdf_path
envman add --key DROPBOX_DIR --value $now
```

これで実行してちゃんと動くかを確認します。

### PDFの配布

`deploy-to-bitrise-io` ステップは失敗するので削除します。
今回は著者陣の過去の経験からDropboxでシェアする様にしました。

<img width="847" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846/ba13c09c-cfc7-ae92-140d-45359d1e54f2.png">

[Dropbox console](https://www.dropbox.com/developers/apps?_tk=pilot_lp&_ad=topbar4&_camp=myapps)でアプリを作成します。
設定を進めていき `OAuth 2` セクションの `Generate access token` の `Generate` ボタンをクリック。

<img width="827" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846/1d4f96d3-8e00-2948-8bbf-916858abef59.png">

表示されるトークンをコピーしてBitriseの `Secrets` に追加します。

<img width="1522" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846/e25558cc-54c4-3a1d-f9f0-64428537d43c.png">

`Workflow Editor` で `Send file to dropbox` ステップを追加

<img width="825" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846/f055922d-275d-6609-519a-3ec12bf0b632.png">

`The secret access token` に先ほど設定した `$DROPBOX_API_KEY` を設定、`Local File` に `$PDF_PATH` 、 `Dropbox remote target` には適当なディレクトリ名で良いのですが今回は `/$DROPBOX_DIR/` を設定しました。

<img width="927" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846/aeebedb6-8f5c-5c6f-b10d-81ae19a33e65.png">

これでビルドすると

<img width="713" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846/f285d185-8504-836e-8ea1-f50c668abdf3.png">

の様な感じでディレクトリが作成されて本が出来ていきます。
あとはSlackなどに通知を設定したりしていきましょう。

## おまけ

PDFがBitriseから書き出せる様になったことで新たな要望が出てきてしまいました。

<img width="986" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846/912c60b4-658d-05ab-11b5-d53878310bdb.png">

### ページ数

[PDFtk](https://www.pdflabs.com/tools/pdftk-the-pdf-toolkit/)を利用すればページ数が取得出来ることが分かったので利用しました。

```sh
apt install -y pdftk
pages=$(pdftk $pdf_path dump_data | grep NumberOfPages)
```

### TODOリスト

```sh
todo=$(find . -name *.re | xargs grep -n -r "#@# TODO")
```

`TODO` と書かれている箇所を探してリスト化しました。
結果は、

<img width="280" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846/c5b4d506-2859-7cb2-a989-8522a8e64bd7.png">

こんな感じです。

### 今日も1日餃子を食べて頑張りましょう。

もはやBitriseから実行する意義を感じなかったのでSlackで `/remind "今日も1日餃子を食べて頑張りましょう。" at 8AM every day` を実行しておきました。
すると翌日

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">朝から、餃子を食べて頑張りましょう、という圧があったので。 <a href="https://t.co/aTzqbLd841">pic.twitter.com/aTzqbLd841</a></p>&mdash; トビ＠ペンギン村 / みんなで叶える物語 (@tobi462) <a href="https://twitter.com/tobi462/status/1169118261344112642?ref_src=twsrc%5Etfw">September 4, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

まさか実行されるとは（笑）

# まとめ

という事で、ただのレビュワーにも関わらず少しばかり突っ込んでプロジェクトに貢献する事が出来たかなと思います。
勿論[iOSテスト全書](https://peaks.cc/iOS_testing)にはCI/CDの章があり、正しい[Bitrise](https://bitrise.io)の使い方も紹介されています。
ご購入がまだで気になっている方は今からでも是非ご購入下さい！

## P.S.

本を書くための環境が整ってしまった。
何か書くか。