Release Manager の負担を軽減する (Android 編)

Mobile Engineer の @chiiia12 です。今回は Android アプリのリリース作業の改善について紹介します。

Release Managerのしごと

私たち Android チームではアプリのリリース作業を担当するメンバーを Release Manager として当番制で回しています。
Release Manager がリリース時に行うタスクは以下です。

  • Release ブランチを作成する
  • Version Name/Code を上げる
  • 段階的公開の割合を設定する
  • アプリの submit
  • Tag を作成
  • Release ブランチから master ブランチへ merge back する
  • CSチーム広報用の issue の作成
  • 審査が通り次第 Play Console から公開する

リリース作業の一部は既に自動化されていました。例えば Gradle Play Publisher を用いて Google Play への submit はボタン一つでできるようになっていました。それでも手作業のタスクが多く、小さい作業ですが他の開発の間にやることになるので、半日ほどはコンテキストスイッチの多い時間を過ごしていました。

手作業を減らす自動化スクリプトのアップデート

そこで、より手作業を減らすよう、自動化スクリプトをアップデートすることになりました。
改善後の Release Manager の仕事は以下になりました。

  1. Release ブランチをセットアップする Job を実行する

    • Release ブランチの作成
    • Version Name/Code をインクリメントする
    • merge backするための Pull Request を作成する
  2. APK を submit する Job を実行する

    • 段階公開用の parameter 設定
    • Google Play へアプリの submit
    • CS 広報用の issue を作成
    • Tag/Release の作成
  3. 審査が通り次第 Play Console から公開する

3つのステップに縮めることができ、Release Manager の負担を大きく減らすことができました。

今回作成した "Release ブランチをセットアップする Job" と "APK の Submit する Job" は、どちらも Jenkins Job から parameter を入力して実行します。実行した Jenkins Job 経由で CircleCI 上の Job を実行することで手作業のタスクを置き換えています。結果的に実作業は2つの Jenkins Job を実行するのみとなり、手作業タスクを減らすことができました。

blog_release_automatically 001 Submit Job が行っているタスクの図

続いて、今回の自動化スクリプトのアップデートを進めていく中での tips をご紹介します。

Jenkins Jobの parameter を利用する

段階公開の割合・公開するトラックなどの設定は Jenkins Job の parameter を通して動的に変更できるようにしました。 Release ごとに Pull Request を作るなどして手作業で設定しなおしていた作業をなくすことができました。

ss 2020-06-03 at 18 37 23 Jenkins の Job 実行画面です。実行時に段階公開の設定を入力できるように変更しました。ここで入力された値は、Job の Configure -> Execute Shell の部分で環境変数として取り出し使うことができます。

echo $RELEASE_VER
echo $TRACK
echo $FRACTION
echo $RELEASE_STATUS

このパラメータを CircleCI の実行したい Job に渡すことで動的に値を変更することができます。

CircleCI API の build parameter を使う

Jenkins Job から CircleCI の Job を呼び出すには CircleCI API を用いると便利です。

Jenkins Job の Execute Shell の部分で CircleCI の API を呼び出します。

JOB_NAME="android_release"
REPO_NAME="android_repo_name"

curl -o /dev/fd/2 \
    -w '%{http_code}' \
    -u "${CIRCLE_TOKEN}:" \
    -d "build_parameters[CIRCLE_API_PROJECT_TOKEN]=${CIRCLE_TOKEN}" \
    -d "build_parameters[CIRCLE_JOB]=${JOB_NAME}" \
    -d "build_parameters[FRACTION]=${FRACTION}" \
    -d "build_parameters[TRACK]=${TRACK}" \
    -d "build_parameters[RELEASE_STATUS]=${RELEASE_STATUS}" \
    "https://circleci.com/api/v1.1/project/github/quipper/${REPO_NAME}/tree/release/${RELEASE_VER}"

Jenkins Job で取得した値は、build_parameters に追加することで CircleCI 上の Job に渡すことができます。今回は公開トラックや公開割合、リリースのステータスを CircleCI の Job 側で使えるようパラメータとして渡しています。 CIRCLE_JOB には実行したい CircleCI 上の Job の名前を設定します。

Gradle Play Publisher の設定値を動的に変える

build_parameters で渡されてきた値を、公開トラックや公開割合の設定に反映させます。私達が使っている Gradle Play Publisher は build.gradle に設定を定義しています。 build.gradle 内の設定値を渡されてきた値によって変更できるようにします。

build.gradle の中で動的に値を変更するためには環境変数に設定することで実現できます。

extend_publisher_variable: &extend_publisher_variable
    run:
      name: Extend Publisher Variable
      command: |
        echo $FRACTION
        echo $TRACK
        echo $RELEASE_STATUS
        echo 'export ORG_GRADLE_PROJECT_FRACTION=$FRACTION' >> $BASH_ENV
        echo 'export ORG_GRADLE_PROJECT_TRACK=$TRACK' >> $BASH_ENV
        echo 'export ORG_GRADLE_PROJECT_RELEASE_STATUS=$RELEASE_STATUS' >> $BASH_ENV

config.yml ではアプリの submit タスク ./gradlew publishRelease を実行する前に上のような環境変数の設定を定義します。

ORG_GRADLE_PROJECT_TRACK のように、prefix に OEG_GRADLE_PROJECT を追加した形で設定すると build.gradle の中で読み込むことができます。例えば ORG_GRADLE_PROJECT_TRACK は build.gradle の中ではTRACKとして読み込むことができます。

build.gradle の中の設定は以下のように変わりました。

play {
    serviceAccountCredentials = file('api-project.json') // This will be created on CircleCI
    if (project.hasProperty('TRACK')) {
        track = TRACK
    } else {
        track = 'production'
    }
    if (project.hasProperty('FRACTION')) {
        userFraction = FRACTION as Double
    } else {
        userFraction = 0.1
    }
    // Use `inProgress` when you want to use userFraction. Use `completed` when 100% release.
    if (project.hasProperty('RELEASE_STATUS')) {
        releaseStatus = RELEASE_STATUS
    } else {
        releaseStatus = "inProgress"
    }
}

これで毎回 userFractionreleaseStatus のみを変更した Pull Request を作成せずに済みました。

GitHub API を活用する

Github API を用いれば、Tag や Release の作成や、Issue の作成もスクリプトに任せることができます。

私達のチームはリリース時に CS チーム向けに、今回のリリースに含まれる開発 Issue/リリースノート/リリース日などを載せた Issue を作成し共有しています。
この作業を手作業で行おうとすると、共有すべき開発 Issue の漏れが起きるだけでなく、 Issue を検索したりリリースノートをコピーしたり等手間のかかる作業でした。
開発 Issue は Label/Milestone を条件に GitHub APISearch issues and pull requests を用いて URL のリストを取得しました。

Tag と紐付いた Release を作成することも Create release API を用いて行っています。
これまでは手元で Tag を作成し、 Web page から Release を作成していました。この Release を作成する手順を忘れることが多いので、自動化によって防げるようになったのは助かっています。この Create Release API で Release を作成すると Tag も同時に作成されるのでシンプルです。

ss 2020-06-03 at 18 27 35 アプリの submit が完了をすると CS 広報用の Issue と Release が自動で作成されるようになりました。

ss 2020-06-05 at 14 47 15 自動で作られた CS 広報用の Issue

おわりに

手作業の多いリリース周りの作業の自動化についてご紹介しました。 Google Play への公開/更新に審査期間が導入されたこともあり完全な自動化とまではいきませんが、少しでも手作業の煩わしさ、開発に使える時間/効率の低下を少しでも防げるよう改善を続けていきたいと思います。参考になれば嬉しいです。