Google+ もご覧ください
ユーザーアイコン

iOSアプリ開発こぼれ話

Jenkins vs Bot。実際使ってみて分かった利点&欠点

Sonson

Jenkins vs Bot。実際使ってみて分かった利点&欠点

Bot

Xcode 5からOS X Serverと組み合わせてCIを実現するBotが追加されました。Botは長らくJenkinsとXcodeのコマンドラインツールを組み合わせて生き延びてきたCIコーダーたちにとって福音となるかと思われた機能です。

しかしそこはAppleというべきか流石の独自路線突っ走りで、Jenkinsのような自由度がまったくなくBotの利点があまりないのが現状です。Botの唯一といえる利点は、実機でのテストをCIに組み込めることです。

とは言えMacとの親和性が高くWebページからAdHocの配布を簡単にできたりと、拡張性を考えないならわりと使える面もあります。そこで今回は、Botを使って自動的にビルド&TestFlightアップロードができる仕組みを作るために、Botのことを少し探ってみることにします。

Bot from Apple
Bot from Apple

VS

Jenkins
Jenkins

Bot自体リリースされたばかりのアプリケーションであまり情報がありませんが、有志の手によっていくばくかの情報が出回りつつあります(12)。そこで私は先に公開された情報を元にBotの活動内容を探ってみた結果について調べてみました。この記事に掲載するBot経由でTestFlightにバイナリをアップロードするスクリプトのベースは、これらの公開されているブログのスクリプトをベースに作りました。

以下から説明していくBotのインストール方法やセットアップ方法については他のサイトを参照してください。Botには、OS X ServerをApp Storeから購入する必要がありますが、開発者向けに無料でダウンロードできるコードが用意されています。詳しくは、iOS Developer Centerにログインして確認してください。

Botが証明書を利用できるようにする

Botは_teamsserverというユーザによって実行されているようです。このMac上のユーザは通常Xcodeでコードを書いているあなた自身のユーザと異なるはずですから、バイナリの署名に使う証明書や秘密鍵をMac上の他のユーザのキーチェーンからも利用できるように変更する必要があります。このため、Botを実行する環境を誰でも使用できるようなマシンでセットアップすることはセキュリティ上で推奨できません(だと思うんですけどねぇ…。他のサイトでこの点についてあまり言及されていないのが気になります)。

キーチェーンのウィンドウの左ペインには、ログインとシステムの二つがあるはずです。通常、あなたが作成したり使用している証明書や秘密鍵はログインの方に保存されています。このログインに入っている項目をシステムにドラッグ&ドロップします。自分の管理するログインの中に項目を残しておきたい場合は、optionキーを押しながらドラッグ&ドロップして項目をコピーするようにしましょう。

Keychain
Keychain

Botにプロジェクトを登録する

これは大した作業ではないので、さらっと流れを説明します。

  1. リポジトリをServerに登録する
  2. Botにリポジトリを登録する

リポジトリをサーバに登録
リポジトリをサーバに登録

Botにリポジトリを登録
Botにリポジトリを登録

これで完了です。

Botに任意のスクリプトを実行させる

残念ながら、BotにはJenkinsのようなプラグインをセットする仕組みが用意されていません。このため、Xcodeのビルド後やアーカイブ後に実行できるスクリプトに自分のやりたい処理を書き、Botに実行させることにします。しかしそこで大きな疑問が…。一体Botさんはどこで作業しているのでしょうか。それがわからないとスクリプトの出だしをつかむこともできません。

Botの活動場所は/Library/Server/Xcodeにあります。このパスの中身のDataのBotRunというのが怪しい感じがします。BotRunの中身はすでにBotでビルドしたことがあれば、複数のBotRun-(Hash).bundle、Cache、Latestの3種類のディレクトリがあるはずです。このBotRun-(Hash).bundleがXcodeのビルド毎に生成されるもののようです。

ディレクトリの一覧からどれが最新かわからないですが、どうやらLatestというディレクトリは最新ビルドへのシンボリックリンクになっているようなので、それを使います。Xcodeでアーカイブを実行した場合、BotRun-(Hash).bundle/output/Archive.xcarchiveというディレクトリが生成されて、そこにipaファイルやdSYMファイルが生成されるようです。これでBotによって生成されるファイルの所在がわかりました。続いて、一度CIを実行してみましょう。

ビルドできない・・・・!!!

おそらくビルドできません。エラーログを見るとわかりますが、これはプロビジョニングファイルの問題です。

ServerのXcodeの設定でチームIDをセットし、デバッグビルドでアーカイブしている場合はおそらくビルドできているはずです。その理由はチームIDをXcodeに設定しておくと、そのアカウントにひもづけられたプロビジョニングプロファイルがServerによってAppleから自動的にダウンロードされ、しかるべき場所に保存されるからです。Serverは、/Library/Server/Xcode/Data/ProvisioningProfiles/にダウンロードしたプロファイルを保存します。どうやらBotから起動されるXcodeは、このパスにあるプロビジョニングプロファイルを利用するようです。

そこでServerが自動的にダウンロードしてくれるプロビジョニングプロファイル以外を利用する場合は、自分で利用するプロファイルをここに置いておきましょう。このパスは普通のユーザには権限が与えられていないので、sudoなどを使ってコピーしたりしないといけないことに注意してください。また、毎回プロビジョニングプロファイルをコピーするのが面倒くさい場合にはシンボリックリンクを張ってあげるのもいいかもしれません。

やっとスクリプトを書く

ここでまで来て、ようやくプロジェクトのPost-actionsに入力するスクリプトを書くステージに来ました。スクリプトは前述のようにXcodeからEdit schemeを選んで、Archiveのアクションをクリックして開くと表示されるPost-actionsのところに書き込むことで設定できます(下図参照)。Archiveである理由は、Botはテスト/プロファイリング/アーカイブのアクションしか実行できないためです。

Post Action
Post Action

まず、アーカイブが正しく実行された後にTestFlightへアップロードするために必要な処理を箇条書きにしてみます。

  1. /tmpパス(作業用)にArchiveで生成されたファイルを丸ごとコピーする
  2. TestFlightにアップロードするdSYMファイル(正しくは実態はフォルダで、バンドルですが)をzipで圧縮する。
  3. ビルドされた.appファイル(こちらも正しくは実態はフォルダでバンドルですが)に配布用の署名をする(AdHoc配布用)。
  4. (必要であれば)gitのコミットログをTestFlightのノートとしてアップロードするためのデータを取得する。
  5. curlでTestFlightへデータを送信する。

本来ならばビルドした.appファイルをそのままzipで圧縮してアップロードしてしまいたいところですが、BotはArchiveしか実行できないのでArchiveの実行結果を使うことになります。ビルドされた配布用の.appファイルと.dSYMファイルを処理するために、まず作業用ディレクトリとして/tmpにビルド結果であるArchive.xcarchiveをディレクトリごとコピーします。Archive.xcarchiveも実体はディレクトリであり、バンドルのようです。

まず、このArchive.xcarchiveの中の./dSYMs/以下にあるdSYMファイルをTestFlightにアップロードするためにzipで圧縮します。次にアップロード用のipaファイルを作成します。理由がよくわかっていないのですが、Archiveのときの署名をAdHocにしておいても、一度この手順を踏んでipaファイルを作成しないとTestFlightにアップロードしても配布ができません(署名がエラーになってしまいます)。xcrunコマンドで生成したipaファイルに含まれる実行ファイルと元々のバイナリファイルは、diffをとってもやはり異なることがわかるので、xcrunで何かしら署名を追加しているようです。

xcrunコマンドで指定する署名は、キーチェーンに表示されるiPhone Distribution: XXXXXXX(XXXXXXXX)のようなタイトルで指定します。また、プロビジョニングファイルは/Library/Server/Xcode/Data/ProvisioningProfiles/に前述の手順でコピーしておいたプロビジョニングプロファイルのフルパスを指定するといいでしょう。

Botの実行権限は_teamsserverなので、他のプロビジョニングプロファイルを使う場合はこのユーザのアクセス権に気をつけて設定してください。

そうした手順を踏まえてできあがったのが以下のスクリプトです。

#
# Original script here,
http://matt.vlasach.com/xcode-bots-hosted-git-repositories-and-automated-testflight-builds/
#

# 変数・パス
TEAM_TOKEN="<YOUR TEAM TOKEN>"
API_TOKEN="<YOUR API TOKEN>"
DISTRIBUTION_LISTS="<YOUR LIST NAME>"
DSYM="/tmp/Archive.xcarchive/dSYMs/${PRODUCT_NAME}.app.dSYM"
IPA="/tmp/Archive.xcarchive/${PRODUCT_NAME}.ipa"
APP="/tmp/Archive.xcarchive/Products/Applications/${PRODUCT_NAME}.app"

# 前回の処理のファイルを消す
/bin/rm -rf /tmp/Archive.xcarchive*
/bin/rm "${DSYM}.zip"

# ビルドしたファイルをコピーする
/bin/cp -Rp "/Library/Server/Xcode/Data/BotRuns/Latest/output/Archive.xcarchive"
"/tmp/"

# dSYMをzipで圧縮する
/usr/bin/zip -r "${DSYM}.zip" "${DSYM}"

# ビルドしたアプリケーションを署名して、IPAファイルを作る
/usr/bin/xcrun -sdk iphoneos PackageApplication -v "${APP}" -o
"${IPA}" --sign "<YOUR SIGN TITLE>" --embed
"/Library/Server/Xcode/Data/ProvisioningProfiles/<PROVISIONING FILE>"

# GITのコミットログを取得する
commitLog=`cd ${SRCROOT};git log -1 --pretty='%s';`

# TestFligtにアップロードする
/usr/bin/curl "http://testflightapp.com/api/builds.json" \
-F file=@"${IPA}" \
-F dsym=@"${DSYM}.zip" \
-F api_token="${API_TOKEN}" \
-F team_token="${TEAM_TOKEN}" \
-F distribution_lists="${DISTRIBUTION_LISTS}" \
-F notes="${commitLog}"
-F replace=True

まとめ~Jenkinsか、Botか

結論から言うと、Jenkinsです。Botなら認証周り、署名周りがもうちょっと使いやすくなるかと思っていましたが、Jenkinsとさほど変わりませんでした。また、Botは実行権限もJenkinsばりにとっつきにくいですので、キーチェーン周りのJenkinsの使いにくさもクリアになっていません。

Jenkinsに対する優位性はとしては以下があります。

  1. 実機でテストできる。
  2. セットアップが(多少)簡単。とっつきやすい(気がします)。
  3. ビルドしたバイナリの配布が簡単(ビルド結果ページにリンクされる)。

といったところでしょうか。

サーバとクライアントを連携させるようなシステムのテストを考えるとBotでは簡単に対応しきれないので、Jenkins + 実機テスト用にBotのようなシステム構成が最適解なのかもしれません。とは言え、実機テストできるという利点を生かすために一度Botにトライしてみてはいかがでしょうか。

参考文献

Ios talk
タグ:

記事をリクエストする

関連記事

コメント