この記事はFablic Advent Calendar 2015の16日目のエントリです。
こんにちは@huinです。 Fablic, Inc.にジョインして(iOSエンジニアからAndroidにジョブチェンジして)もうすぐ1年が経過しようとしています。
今年1番の思い出というとGoogle I/OとWWDCの現地参加が挙がりますが、開発面だとCircleCIの導入(正確にはTravisCIからの移行)が良い思い出です。
今年の前半はCircleCI導入の話やチューニングの話を外で発表していたのですが、最近の話はしていなかったので良い機会だということでまとめてみたいと思います。
ちなみにAndroidプロジェクトのお話です。
Fablicの開発フロー
まずはFRIL Androidの開発フローでCircleCIをどのように利用しているか、簡単に説明します。
まずメンバーは作業単位毎にブランチを作ってGithubにプッシュしています(①)。CircleCIはプッシュをトリガーにして変更のあったブランチでユニットテストを走らせます(②)。テスト結果はすべてSlackに通知され、テストを通過していることを確認した上で変更はdevelopブランチにマージされます。
また、masterとdevelopブランチはPRのマージ後にテストが実行されますがテストを通った場合にはapkを作成してFabric.io Betaにアップロードします。こうすることで常に最新のビルドをDLして動作を確認することができます。
このようにCircleCIは開発フローの中心になっているのでCircleCIが安定かつ高速になるほど僕らの開発速度は向上します。逆に、CircleCIの動作が不安定になると僕らの開発も滞ります。
というわけでCircleCIの重要性を説明したところでCircleCIをより効率的に運用するためのTipsを紹介したいと思います。
CircleCIのTips
1. Project Settingsのオーバーライドを活用する
CircleCIはGithubからソースコードを取得してジョブを実行するので、ジョブの設定などを変更した場合には設定ファイルであるcircle.yml
をコミットしてGithubにプッシュする必要があります。ジョブの設定がこなれてくるまでは何度も設定を書き換えてトライ&エラーする必要がありますが、そのたびにコミットしているとgitのコミットログが膨大になります。Githubにプッシュする時間もバカになりません。
そういった場合にはcircle.yml
を編集するのではなく、CircleCIのProject Settings > Test Commandsに処理を書いてジョブを実行しましょう。
ここに処理を書くとcircle.yml
の内容に加えて実行されます。テストのオプションや、ライブラリのダウンロードなど細かい変更はいったんここに書いてテストし、上手く通ったらcircle.yml
に追記するようにすると効率的にセットアップできます。
2. 処理はスクリプトに分離する
これは細かいTipsですが、テストやapkの配布などの処理をまとめてシェルスクリプトに書いておき、CircleCIの書くディレクティブ(dependencies:
, test:
, deploy:
)でスクリプトを実行するようにします。
こうすることで、CircleCIと同じ処理をローカルでも実行できます。例えば複数のProductFlavorでテストを実行したいときは、自分でgradleタスクを入力するよりもスクリプトを実行するほうが早くなるはずです。
FRIL AndroidではテストとFabricへのアップロード部分を以下の様にスクリプトに分離して実行しています。
circle.yml
test: override: - ./scripts/run-tests.sh deployment: fabric: branch: [master, develop] commands: - ./scripts/distribute-fabric.sh
3. 保存するキャッシュは最小限にする
CircleCIではジョブで必要になるライブラリなどをキャッシュしておき、次回以降のジョブで使いまわしてくれる機能があります。dependencies:
ディレクティブでキャッシュするディレクトリを書くだけで設定できるので便利ですが、安易に多くのファイルをキャッシュすると、保存と復元に時間がかかってしまいかえってジョブの実行時間を延ばすことになります。
AndroidではビルドツールやSDKのバージョンなどがプロジェクトによってまちまちになると思いますが、追加でインストールするのは必要なものだけにし、キャッシュ量を最小限にするようにしましょう。
CircleCIの実行環境で最初から入っているツール類はドキュメントに書かれていますので参考にしてみてください。
4. Robolectricで高速化
冒頭でも述べましたが、ジョブの高速化は開発速度に直結します。
処理を高速化させたい時最も遅い部分を見極めるのが大事ですが、Androidプロジェクトの場合おそらくジョブの中で最も時間をとるのがエミュレータの起動ではないかと思います。実際FRILではエミュレータの起動だけで3分以上かかり、エミュレータ上でのテスト実行でも長い時間を必要としていました。(下図のcircle-android wait-for-boot
がそうです)
エミュレータ起動の時間
またジョブ全体でみると一度の実行に20分ほどかかっていました(下図)。 そもそも仮想環境(CircleCIはVMコンテナ内でジョブを実行しています)の中でさらにエミュレータを使うとなると早くないのは仕方ないのですが、小さな変更でもテストの結果を確認するのに20分も待たされるというのは非常にストレスです。
この問題を解決するためにエミュレータを使わずにユニットテストを実行できるRobolectricを導入しました。
改善前の実行時間
Robolectricの導入はそれなりにクセがあってハマりどころが多いので簡単ではないのですが、効果は非常に大きなもので20分かかっていたジョブが約10分へと一気に短縮されました。Fabricへのアップロードを含まない単純なテストだけの場合なら5分前後で完了するまでに早くなっています。下の図が導入前後の実行時間のグラフですが導入前後で約半分になっているのがお分かりいただけると思います。
Robolectric導入前後の実行時間
Android Instrumentation Testの内容を100% Robolectricで実行できるわけではないのですが、もしテストを早くしたいということであればぜひ一度導入を検討すると良いと思います。
5. サポートにはどんどん質問を送る
これは技術的なTipsではないのですが、CircleCIをより使いこなすためにぜひ紹介したいと思います。
CircleCIはチャット形式でサポートに質問できるUIが用意されています(最近こういうサポート窓口が増えてますね)。サポートは全て英語ですが、メールのような堅苦しいフォーマットではなくカジュアルにメッセージが送れるので英語が苦手な方でも積極的に質問を送ると良いと思います。
CircleCIの導入当時はJVMが仮想マシンのメモリを食いつぶしてしまってジョブに失敗する減少が多かったのですが、サポートに連絡すると他のユーザが試していた解決方法(JAVA_OPTS
環境変数の設定)を紹介してくれました。ジョブの実行時間の長さに悩んでいた時もGradleのログからボトルネックになっている処理を突き止めてくれたりとサポートというよりはコンシェルジュに近い感覚でトラブルシューティングをしてくれます。
片言の英語でもエラーメッセージと一緒に送れば何かしらアクションをしてくれるはずなのでぜひ積極的にサポートに連絡してみてください!
まとめ
というわけでCircleCIのTipsを5つ紹介させてもらいました。
こういったCIサービスは、自分の開発機とCI環境の差が大きいので導入でハマってしまって挫折することもあると思います。しかし導入して開発フローの中で自然にまわるようになるとなくてはならない存在になります。特に複数人で開発している場合は変更も多くなるので、なるべく細かい変更で動作を確認していくことで自然と品質が担保されるようになると思います。
テストを書くのが苦手な人もまずはビルドが通るかどうかのチェックで使ってみたり、apk配布を自動化していつでも最新のビルドが確認できる、そんな環境を作ってみてください。安心して開発が進められるようになるはずです。
以上この1年で学んだCircleCIのTipsでした。
明日は弊社デザイナーの @yuki930 が効果を上げるデザインについて書いてくれます。お楽しみに!!