Android アプリを master ブランチマージで Play Store Beta に公開する

このエントリーをはてなブックマークに追加

アプリケーションエンジニアの西辻です。
今回の記事では、弊社内で開発している Android アプリを master ブランチマージで Play Store Beta に公開するまでの方法についてご紹介したいと思います。
また、今回の記事は Qiita Android その2 Advent Calendar 2017 5日目に参加しています。

Overview

大きく以下の項目について書いていこうと思います。

  • はじめに
  • Play Store 公開までの全体の流れ
  • CI 環境の設定方法
  • Play Store Beta に配信するまでの時間について
  • apk ファイルを Play Store に公開するライブラリの設定方法
  • Play Store での公開方法について
  • 半年運用して分かったこと
    • 間違えて apk ファイルを Play Store Beta にあげた時の対応
    • Proguard を有効にした時にアプリがクラッシュする
    • アプリの強制アップデートに関して
  • おわりに

はじめに

Android アプリを開発、運用し始めて半年くらい経ちました。
最初の頃は apk ファイルを手元のマシンでビルドしていたのですが、
ローカルの状態に依存してしまうので、何か設定ファイル間違えてないかとか
ちゃんと master ブランチに移動してから・・・などと
いつもドキドキしながら最後に
Play Store 上でアプリをインストールして確認をしていました。

上記の心配事を解消するために色々組み合わせて
master ブランチにマージしたタイミングで
Play Store Beta まで apk ファイルを公開して Beta テストを行い、
万全を期して Play Store まで公開できるような仕組みができたので
備忘録とチームメンバーへの情報共有を兼ねてこの場で情報をまとめていきます。

Play Store 公開までの全体の流れ

下記に Play Store 公開までの全体の流れを図にしています。
Play Store 公開までの全体の流れ

説明用に 1. から 4. の番号を振っています

1. git push origin master

Play Store Beta まで upload するトリガーとなるコマンドです。
実際には develop ブランチから maser ブランチへの Pull Request を作成し、
JIRA チケットに記載のある改修内容を確認します。
develop ブランチから maser ブランチへの Pull Request の作成と改修内容の確認

2. Bitbucket Pipelines 上で test の実施と apk file のビルドを行います

Bitbucket Pipelines の設定方法については
後述の 「CI 環境の設定方法」で詳細に設定方法などを記述します。

流れとしては Bitbucket 上で管理されている Dockerfile を元にして
Docker Hub から automated builds の設定を入れておきます。
Dockerfile には Android アプリをビルドするために必要なものをあらかじめ
入れておく感じになっています。
実際に利用している Dockerfile は以下になります。
注意点としては docker image に Android SDK を含めて再配布はライセンス違反になるので
Docker Hub 上では private image にする必要があります。
参考: http://gfx.hatenablog.com/entry/2016/06/21/112404

FROM snowdream/gradle:latest

# Install dependencies
RUN dpkg --add-architecture i386 && \
    apt-get -qq update && \
    apt-get -qqy install libc6:i386 libstdc++6:i386 zlib1g:i386 libncurses5:i386 tar git --no-install-recommends && \
    rm -rf /var/lib/apt/lists/*

# Download and untar Android SDK
ENV ANDROID_SDK_URL http://dl.google.com/android/android-sdk_r24.4.1-linux.tgz
RUN curl -sSL "${ANDROID_SDK_URL}" | tar --no-same-owner -xz -C ${SDK_HOME}
ENV ANDROID_HOME ${SDK_HOME}/android-sdk-linux
ENV ANDROID_SDK ${SDK_HOME}/android-sdk-linux
ENV PATH ${ANDROID_HOME}/tools:$ANDROID_HOME/platform-tools:$PATH

# Install Android SDK components
ENV ANDROID_COMPONENTS platform-tools,build-tools-25.0.0,build-tools-24.0.0,build-tools-24.0.1,build-tools-24.0.2,build-tools-23.0.3,android-24,android-23
ENV GOOGLE_COMPONENTS extra-android-m2repository,extra-google-m2repository,174
RUN echo y | android update sdk --no-ui --all --filter "${ANDROID_COMPONENTS}" ; \
    echo y | android update sdk --no-ui --all --filter "${GOOGLE_COMPONENTS}"

Certificates 情報は Android App Repository とは別 Repository で管理しています。
Certificates Repository は Android App Repository よりも更に権限を厳しく設定してあります。

Bitbucket Pipelines で apk をビルドする際に
Certificates Repository を clone してアプリの署名情報や Play Store への認証情報などを取得します。

実際に Certificates Repository が所持しているファイルは以下です。

file name description
release-key.jks keystore file
key.json Play Store への認証情報が管理されています。
keystore.properties keystore file のパスワードやエイリアスなどを保存するシンプルなファイルです。

3. Docker Hub 上で automated builds の設定を入れる

2. でほとんど書いてしまいましたが、master ブランチに push されると
Docker Hub 上で docker image が build されます。
この docker image を利用して Bitbucket Pipelines 上で apk ファイルを作成しています。

4. Play Store Beta 上で最終動作確認とアプリ公開

Bitbucket Pipelines が完了すると Play Store Beta 上にアプリがアップロードされています。
Play Store Beta

Play Store Beta に反映まで少しタイムラグがあるので
Bitbucket Pipelines 完了後すぐに Play Store へアクセスしても
古いバージョンが見えていることが多いです。

体感ベースですが Google Play Console に登録しているメールアドレス宛に
公開完了通知メールがくるとほぼ更新完了しています。

設定からアプリ更新の通知を受け取る設定ができます

IFTTTで公開通知を受け取っておくと便利です

ベータ版を管理からベータアプリのテスターを登録できます。
ベータテスターの登録 テスターの追加は簡単でテスターの gmail アドレスを登録して
テスターとして認証するためのオプトイン URL を踏んでもらうだけです。

テスターとしてこのあと実際に公開されるアプリを一通り触ってみて
問題がなければ製品版として公開します。

右上の「製品版にリリース」ボタンを押します。
ベータで検証されたアプリがそのまま公開されるので安心です

あとは「確認」ボタンを押していくだけで製品版として公開できます。
リリースノートなども後述のプラグインで設定可能です

CI 環境の設定方法

CI 環境と書いていますが、ここでは Bitbucket Pipelines を指しています。
docker ベースの CI 環境であれば Bitbucket Pipelines でなくとも
自身のプラットフォームに合わせて調整可能かと思います。

では、実際に利用している bitbucket-pipelines.yml が以下になります。

image:
  name: housmart/android-ci:latest
  username: $DOCKER_HUB_USERNAME
  password: $DOCKER_HUB_PASSWORD

pipelines:
  branches:
    master:
      - step:
          caches:
            - gradle
          script:
            - mkdir -p ~/.ssh
            - (umask  077 ; echo ${MY_SSH_KEY} | base64 --decode > ~/.ssh/id_rsa)
            - git clone ssh://git@bitbucket.org/Housmart-Inc/android_certificates.git
            - cp android_certificates/kawlu-release-key.jks app/
            - cp android_certificates/key.json app/
            - cp android_certificates/keystore.properties .
            - mkdir /usr/local/android-sdk-linux/licenses
            - echo ${ANDROID_SDK_LICENSE} > /usr/local/android-sdk-linux/licenses/android-sdk-license
            - ./gradlew testDebug
            - git log -3 --pretty=format:"%s, %an, %cd" > release_notes.txt
            - VERSION_NAME=`grep versionName app/build.gradle | sed s/\"//g | awk '{print $2}'`
            - git tag ${VERSION_NAME}
            - git push origin :${VERSION_NAME}
            - git push origin --tags
            - ./gradlew assembleRelease crashlyticsUploadDistributionRelease
            - ./gradlew publishApkRelease
  default:
    - step:
        caches:
            - gradle
        script:
            - mkdir /usr/local/android-sdk-linux/licenses
            - echo ${ANDROID_SDK_LICENSE} > /usr/local/android-sdk-linux/licenses/android-sdk-license
            - ./gradlew testDebug
            - git log -3 --pretty=format:"%s, %an, %cd" > release_notes.txt
            - sed -i "s/versionCode [0-9]/versionCode `date +"%-m%d%H%M"`/" app/build.gradle
            - cp keystore/debug.keystore ~/.android
            - ./gradlew assembleDebug crashlyticsUploadDistributionDebug
            - mkdir -p ~/.ssh
            - (umask  077 ; echo ${MY_SSH_KEY} | base64 --decode > ~/.ssh/id_rsa)
            - git clone ssh://git@bitbucket.org/Housmart-Inc/android_certificates.git
            - cp android_certificates/kawlu-release-key.jks app/
            - cp android_certificates/key.json app/
            - cp android_certificates/keystore.properties .
            - ./gradlew assembleRelease crashlyticsUploadDistributionRelease

順を追って説明をしていきます。

image:
  name: housmart/android-ci:latest
  username: $DOCKER_HUB_USERNAME
  password: $DOCKER_HUB_PASSWORD

3. で automated builds されている image になります。
Bitbucket Pipelines が動作するトリガーとしては
master ブランチだけ Play Store Beta にあげるのでその処理を追加してあげているのと
それ以外のブランチでは Fabric Beta に apk をアップロードする仕組みにしています。
private image なので pull するための認証情報を加えています。
本記事では master ブランチのステップのみを取り上げて詳細に説明をしていきます。

        caches:
            - gradle

Bitbucket Pipelines では指定したディレクトリをキャッシュし、
次回動作時に再利用することが可能です。
gradle の場合はデフォルトの設定でキャッシュできるので設定はシンプルです。
gradle 以外でもデフォルトで指定できるものは以下に公式ドキュメントがあります。
https://confluence.atlassian.com/bitbucket/caching-dependencies-895552876.html
自分で指定したディレクトリにキャッシュさせたい場合も上記公式ドキュメントに設定方法が記載されているので
参考にできるかと思います。

        script:
          - mkdir -p ~/.ssh
          - (umask  077 ; echo ${MY_SSH_KEY} | base64 --decode > ~/.ssh/id_rsa)
          - git clone ssh://git@bitbucket.org/Housmart-Inc/android_certificates.git
          - cp android_certificates/kawlu-release-key.jks app/
          - cp android_certificates/key.json app/
          - cp android_certificates/keystore.properties .

apk ファイルのビルドに必要な Certificates Repository を clone するために
ssh の設定をしています。
この辺りは、公式ドキュメント通りに設定しています。
https://confluence.atlassian.com/bitbucket/use-ssh-keys-in-bitbucket-pipelines-847452940.html
Certificates Repository を clone したら
適切な場所にファイルをコピーして設置します。

          - mkdir /usr/local/android-sdk-linux/licenses
          - echo ${ANDROID_SDK_LICENSE} > /usr/local/android-sdk-linux/licenses/android-sdk-license

apk をビルドする際に android-sdk-license が必要になるので設定します。
自分のホストマシンにあるライセンスを流用しています。
${ANDROID_SDK_LICENSE} は Bitbucket Pipelines の環境変数(Secured)として設定しています。

          - ./gradlew testDebug

テストの実施です。

          - git log -3 --pretty=format:"%s, %an, %cd" > release_notes.txt
          - VERSION_NAME=`grep versionName app/build.gradle | sed s/\"//g | awk '{print $2}'`
          - git tag ${VERSION_NAME}
          - git push origin :${VERSION_NAME}
          - git push origin --tags

release_notes.txt は Fabric Beta に必要なので過去のコミットログを出しています。
VERSION_NAME は app/build.gradle から拾ってきて
リリースタグとして利用しています。

          - ./gradlew assembleRelease crashlyticsUploadDistributionRelease

Fabric Beta に製品版として upload します。
過去のバージョンでバグが発生した際に Fabric Beta に
upload された apk ファイルからバグの再現性を確認するのに利用します。

          - ./gradlew publishApkRelease

Play Store Beta に apk ファイルを upload します。
Bitbucket Pipelines 上は1行ですが、実際には gradle の設定などが必要なので
後述の「apk ファイルを Play Store に公開するライブラリの設定方法」で設定方法について詳細を記載します。

Play Store Beta に配信するまでの時間について

だいたい 3 分くらいで終わります。
Bitbucket Pipelines Step Detail Sample 過去の Bitbucket Pipelines 実行時間 ベースの image が docker 化されているのと、gradle cache、Bitbucket Pipelines の性能などが要因かと思いますが
特にストレスなく動作するので開発しやすいです。

apk ファイルを Play Store に公開するライブラリの設定方法

Triple-T/gradle-play-publisher を利用しています。
設定方法は README に詳しく書いているのでその通りに設定したら問題ないです。
注意点としては Specify the track にある track を beta にすることです。
このプラグインを利用することでリリースノートも一緒に Play Store Beta まで配信してくれるので
Pull Request ベースでリリースノートがレビューできるのもいい感じです。

半年運用して分かったこと

間違えて apk ファイルを Play Store Beta にあげた時の対応

これは避けたい事象ではあるのですが、たまに確認が漏れていて Play Store Beta まで
あげてしまってしまうことがあります。
そんな時は versionCode を1つあげるしかなさそうです。
Play Store Beta にすでに上がっている versionCode は利用できないです。

Proguard を有効にした時にアプリがクラッシュする

新しいライブラリを入れて Progurad の設定を忘れてたりすると
製品版でクラッシュしたりします。
これを防ぐために基本的に全ての Pull Request に対して Progurad ありでビルドして
Fabric Beta で確認ができるようにしています。
まあ、毎回 Fabric Beta で確認はしんどいので新しいライブラリ追加したときくらいですかね。

アプリの強制アップデートに関して

今回の記事と全く関係ないですが、入れておくと何か困った時に助かります。

おわりに

長々と書きましたが、「Android アプリを master ブランチマージで Play Store Beta に公開する」というタイトルに沿った記事でした。
個人的には heroku ライクな環境になったので結構満足しています。
apk ファイルの作成から Play Store への公開作業は繰り返しの作業になるので
繰り返しの作業が得意なコンピュータに任せていきたいですね。
本記事では CI として Bitbucket Pipelines を利用していますが、
別の CI でも応用はできると思うのでみなさんの Android 開発環境の改善のきっかけになれば幸いです。


Housmartでは不動産業界を変えるカウルを支えるエンジニアを募集しています。
今話題のReTech!業界を変えるカウルを支えるエンジニアをWanted!

このエントリーをはてなブックマークに追加