ipa をビルドしてWebサイトで配布するというのを自動化した話


# はじめに
ipa の作成からWebサイトでのInHouse配布までの流れを自動化した話です。
構成は最終的に Node.js + Express4 + Azure Websites + Azure Storage になりました。
なにかの参考になれば。

# 要件
定期的に特定のブランチのスナップショットをベータ版としてテストユーザ向けに配布する。
配布先は、DeployGateと自前のインストールサイト。

DeployGateへのアップロードはshenzhen を利用すると簡単に行うことができる。
こんな感じ。

```
ipa distribute:deploygate \
  -a ${DEPLOY_GATE_API_KEY} \
  -u ${DEPLOY_GATE_USER_NAME} \
  -u ${pathToIpa} \
  -u ${deployComment}
```

問題はインストールサイトで、これはアップロード用のAPIからダウンロードさせるページまで全部自作しなければならなかった。
（なぜDeployGateだけでだめなのかというと、使いこなせない人がいるから。）
なおこれまでのインストールサイトは静的リソースが置いてあるだけで、更新はサーバにログインして30分くらいかけて手作業で行っていたとのことだった。
まあこれは、自動化できるでしょう、ということでチャレンジしてみた。

以降、インストールサイトの構築方法についてまとめる。

# OTA(over the air) InHouse 配布について
そもそもWebからインストールさせるということをやったことがなかったのでそこの仕組みから調べた。
下記のような記事を参考にした。

- [iOS7.1以降端末へのOTA配信](http://qiita.com/nofrmm/items/62f0adf0268b87e075e6)

つまり、

0. ユーザがリンク（itms-service のリンクスキーム）をクリック
1. App Storeが起動して指定されたplistを取得
2. App StoreがplistにかかれたURLからipaをダウンロードしてインストール

という流れになるようだ。

これを実現するには、plistとipa を直接ファイルとしてダウンロードできるようにどこかに置いておかないといけない。
ちなみにGETリクエストのレスポンスに書き出すというのではダメだった。この辺は勉強不足でよくわからない。
Azure Storage のPublic Blob はREST APIで直接アクセスして取得できるので、これにうってつけだった。
というか他のこういうサービスを知らない。

# どうやって作ったかおよび雑感
## Node.js
特に筆者はWEBに詳しいわけではないが、とっつきやすそうだしコード量も少なくて済みそうだったので Node.jsにした。
以前Heroku のチュートリアルで入手したプロジェクトがあったのでそれを元に改造した。

ひとつ困ったのは、Node.js で非同期処理の問い合わせを同期的に待つことがちょっと難しいということ。
結局、Callback の中に処理を書いてそのCallbackの中に処理を。。というNode.jsではお馴染み（らしい）のパターンになった。
ちょっと可読性が悪い。

そしてやっぱりjavascript特有のゆるふわな型には始終惑わされた。
Javaで書けとは言わないが、Scalaくらい試してみてもよかったかもしれない。

配列処理はやはりmapとかfilterとか直感的に書けてよかった。

```javascript
array.map(function(item) {
  item.number = count++;
  return item;
});
```


Node.js ですばらしいと思ったのは、サードパーティのモジュール（つまりライブラリ）が充実していて、どれもAPIがシンプルでスーパー使いやすいというところ。

## 用意したエンドポイント
### /upload
ipaファイルのアップロード用。
パラメータに指定されたバージョンとかを元にplistも生成してBlobにつっこむ。

テーブル構成について少し迷ったが、ファイル名を```${APP}_${TARGET}_${COMMIT_HASH}_${DATE}.ipa``` (これはアップする側が指定する) として、これをpkeyにすることにした。
バージョンなどの情報をテーブルに保存していたとしても、結局ファイル自体を入れ替えられてしまうと食い違うことになってしまうので、ファイルは不変であるという前提に基づく。
ファイルの不変性を保証する仕組みはないが、その辺は運用でカバー。
まあアップされた日がわかればいいだけなので。

ちなみにplistは上記のpkey + .plist という形で生成している。

デプロイ完了したらメールで通知して欲しいと言われたので、nodemailerというモジュールを使った。
めちゃくちゃ便利で実装は一瞬だった。

### /
ダウンロードさせるページ。
Blobにあるipaをアップされた日の降順で並べて表示するだけ。

ベーシック認証をかけたかったが、[basic-auth-connect](https://www.npmjs.com/package/basic-auth-connect)というモジュールを使ったらこれも一瞬だった。

せっかくなのでBootstrap のthemeを使ってシャレオツみたいにしてみた。
なんとなく選んだらUbuntu で使われているやつだった。

### /cert
上の記事にもあるが、iOS7.1以降はitms-serviceのdownload-manifestのURLをhttpsにしてかつサーバから証明書を発行する必要がある。
[この記事](http://marchenterprise.net/2014/03/12/post-211/)の通りにやって、server.derをサーバに配置、このエンドポイントで別途ダウンロードさせるようにした。
つまりインストールサイトのトップには、「証明書をダウンロード」と「アプリのダウンロード」の2種類のリンクが存在する。
なんかかっこわるいけどきっとこういうもんなんだろう。

# 定期的に自動でビルドする
## deploy.sh
getopt コマンドを利用して色々なオプションをつけてみた。

-a : Archiveしない
-s : ユーザにプロンプトしない

とか。-as といった使い方もできてコマンド感が増してよい。

ipaのアップロードもcurlを使って自動でやっている。

```bash
curl -v -X POST -F "ipa=@${pathToIpa}" "http://${host}/upload?..."
```

## 起動
シェルを一本叩くだけなのでcronでやろうかと思ったが、[cronから起動した場合keychainがうまく見れない](http://goo.gl/FblL4x)という問題があるらしく、ビルドができなかった。
あまり余計なものは使いたくなかったが、諦めてJenkinsを利用。
cronでハマったような問題はなく、普通に動いた。優秀。

しかし、最終的に使ってもらう段になって、「CLIとか無理」と言われた。
以前はOSXのAutomatorとかいうアプリでデプロイの自動化を実現していて、割と普通のデスクトップアプリっぽく使えてたので落差があるようだった。
中身はシェルなのにね。
現在はdeploy.sh に丁寧なREADME.md をつけた上でJenkinsにジョブのテンプレートを作ってなんとか触ってもらっている。
ここのところも本当はCircleCI + Hubot + Slack みたいな構成でChatOpsしたいところ。

# Azure について
azure-cli が予想以上によかった。
サーバのログをtail する、とか普通にある。

```bash
azure site log tail app
```

Node.js向けのSDK はソースコードも公開されているので、リファレンス見てもよくわからない場合にかなり助かった。

ソースコードのデプロイについても、git push するだけでデプロイが走ったので、ストレスなく開発できた。
個人的には管理画面がAWSみたいにごちゃごちゃしていないのもよかった。
会社のアレなので料金とかは知らないが、条件が許すのであればオススメできる。

おわり。