entry-header-eye-catch.html
entry-title-container.html

entry-header-author-info.html
Article by

CIで Androidアプリのライブラリ更新を楽にする

こんにちは。ピクシブ株式会社で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さえ使っていれば連携するだけで利用できます。 f:id:pxvpxv:20210308105410p:plain   定期的にDependabotがライブラリの更新をチェックしてくれて、新しいバージョンがあるときにPRを自動で作ってくれます。   PRに対する様々な操作はコメントから行うことができ、"majorバージョンが上がるまでPRを作らない" といった設定が可能です。

例えば現在使っているバージョンと最新バージョンが大きく離れており、慎重に時間をかけて上げたいということがあります。このときpatchバージョンなど細かい更新は一旦無視したいため、設定すると便利です。

build.gradleとは別のファイルに定義されたライブラリの更新

Dependabotはgradleファイルに記述されたライブラリについて、バージョン番号を最新に変更したPRを出してくれますが、変数で定義してあっても更新してくれます。

f:id:pxvpxv:20210308105504p:plain

pixiv Androidアプリではライブラリとバージョンの定義を1ファイルに定義し、それを build.gradle から利用する形にしていました。マルチモジュールを導入しており、異なるモジュールでも同じライブラリを用いる場合はバージョンを統一したいためです。

ここがハマリポイントで最初はDependabotがうまく動いてくれなかったのですが、以下の設定で解決しました。

影響調査

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アプリエンジニアを 新卒中途 ともに絶賛募集中です!

recruit.jobcan.jp

recruit.jobcan.jp

icon
verno3632
2017年5月入社。pixivアプリチームでpixiv Androidアプリの開発を担当しています。 CIとアイドルマスターが好き。