こんにちは。ピクシブ株式会社でpixiv Androidアプリの開発を担当しているverno3632です。
今回は弊チームで導入しているライブラリ更新の仕組みについてお話します。
ライブラリ更新の流れ
まずpixiv Androidアプリの開発で利用しているツールを紹介します。基本的にGitHub上で開発を行い、CIにはBitrise、コミュニケーションツールにはSlackを利用しています。
GitHubへの更新をフックにしてBitriseでCIが走り、結果がSlackに投稿されるようになっています。
ライブラリ更新の流れはおおまかに以下のようになっています。
- PR作成
- 影響調査
- マージ
PR作成
アプリ開発にはライブラリの更新作業がつきものですが、以前までは温もりのある手作業で運用されていました。
ライブラリの更新があるかの差分を出す Gradle Versions Plugin を週1回自動で動かし、その結果をSlackに投稿し、これを見てそれぞれのライブラリについて空いた時間に更新していく運用でした。
しかしpixiv Androidアプリの開発チームのプロジェクト運用では、更新タスクがどんどん溜まっていってしまいました。
Dependabot導入
そこでDependabotを導入しました。GitHubさえ使っていれば連携するだけで利用できます。 定期的にDependabotがライブラリの更新をチェックしてくれて、新しいバージョンがあるときにPRを自動で作ってくれます。 PRに対する様々な操作はコメントから行うことができ、"majorバージョンが上がるまでPRを作らない" といった設定が可能です。
例えば現在使っているバージョンと最新バージョンが大きく離れており、慎重に時間をかけて上げたいということがあります。このときpatchバージョンなど細かい更新は一旦無視したいため、設定すると便利です。
build.gradleとは別のファイルに定義されたライブラリの更新
Dependabotはgradleファイルに記述されたライブラリについて、バージョン番号を最新に変更したPRを出してくれますが、変数で定義してあっても更新してくれます。
pixiv Androidアプリではライブラリとバージョンの定義を1ファイルに定義し、それを build.gradle
から利用する形にしていました。マルチモジュールを導入しており、異なるモジュールでも同じライブラリを用いる場合はバージョンを統一したいためです。
ここがハマリポイントで最初はDependabotがうまく動いてくれなかったのですが、以下の設定で解決しました。
- 定義ファイル名に
dependencies
を含める (https://github.com/dependabot/feedback/issues/345) - 定義ファイルをルート直下ではなく /gradle 以下に置く
影響調査
Dependabotが作ったPRはノーチェックでマージするわけにはいきません。
差分確認
ライブラリの更新にあたり、どの修正が行われたかを確認する必要があります。通常はライブラリのChangelog等を見る必要がありますが、Dependabotでは自動的に取得できるものについてPRに載せてくれます。
再帰的な依存ライブラリの差分確認
追加で確認しなければいけないのが、対象のライブラリが依存しているライブラリです。
ライブラリの更新内容にその依存ライブラリのバージョンアップが含まれることはよくあります。Changelog等から気づけると良いのですが、見落とすことも多く、その依存ライブラリの更に依存ライブラリまで考慮することは難しいです。また例えばbuild.gradleにライブラリをバージョン1.0で明示的に指定し、他のライブラリがそのライブラリのバージョン1.1に依存している場合、アプリが利用するのは明示的に記述していないバージョンである1.1になってしまうことがあります。
以前は更新前後に ./gradlew app:dependencies
を行い、その差分を手動で確認していました。 ライブラリの依存関係は木構造なので更新があったライブラリはどこから依存されているか見たいのですが、diffコマンドを使うと更新があった行しか表示されません。
そこでライブラリの更新が依存元も合わせて表示される dependency-tree-diff を導入し、CIで自動で動作するようにしました。
以下はBitrise上でCIを走らせる際に用いるスクリプトです。
PRのマージ前後のコミットでの依存関係をファイルに書き出し、 dependency-tree-diff
での差分を環境変数で次のステップに渡しています。次のステップではこの結果をPRへコメントしています。
./gradlew clean :app:dependencies --configuration productionReleaseRuntimeClasspath> $BITRISE_DEPLOY_DIR/new.txt git fetch git checkout origin/$BITRISEIO_GIT_BRANCH_DEST ./gradlew clean :app:dependencies --configuration productionReleaseRuntimeClasspath> $BITRISE_DEPLOY_DIR/old.txt wget https://github.com/JakeWharton/dependency-tree-diff/releases/download/1.1.0/dependency-tree-diff.jar chmod +x dependency-tree-diff.jar ./dependency-tree-diff.jar $BITRISE_DEPLOY_DIR/old.txt $BITRISE_DEPLOY_DIR/new.txt > diff_text cat diff_text if [[ -s diff_text ]]; then echo '```diff' >> quoted cat diff_text >> quoted echo '```' >> quoted envman add --key COMMENT_BODY --valuefile quoted envman add --key EXIST_BODY --value "true" fi
markdownのシンタックスハイライトに diff
を指定することで、差分が色付きでよりわかりやすくなっています。
issueの確認
後から気づいたのですが、以前はライブラリ更新を反映するまでの時間が遅れたことで、ライブラリ側でバグの発見が行われ結果的にバグが発生しているバージョンを避けることができていました。
Dependabot導入でライブラリ更新を即時行えるになりましたが、バグのないアプリをリリースするためにも、PRが作られてもすぐにマージはしていません。2週間ほど様子を見た上で各ライブラリでissueが上がってないか確認しています。その後動作確認を行っています。
マージ
ここまで確認できて晴れてマージすることができます 🎉
例外的に自動マージを許容するパターン
実はDependabotには、あらかじめ指定していたライブラリをビルド成功後に自動でマージする機能があります。
本番に影響無いような、テストやデバッグでのみ使われるライブラリに限ってはマージもDependabotに任せています。特にテスト系のライブラリは頻繁に更新されるので、ここが自動マージできるだけでもライブラリ更新全体のコストはかなり下がります。
まとめ
pixiv Androidアプリでのライブラリ更新についてお話しました。ここではDependabotを用いることでライブラリの更新のコストを下げることができました。また運用方法を工夫したり依存関係の差分を自動で出力することで、より安全なバージョンアップを行っています。
今回は紹介しませんでしたが、他にもZapierのようなタスク自動化ツールやCodeClimateといった静的解析ツールを用いて効率を上げながら開発を行っています。
この記事でピクシブに興味をもった方がいれば是非、弊社で一緒に働きましょう!ピクシブ株式会社では、Androidアプリエンジニアを 新卒、中途 ともに絶賛募集中です!