
【Android】GitHub ActionsからワンクリックでFirebase App Distributionにアプリをデプロイする

IT技術

はじめに
こんにちは!株式会社ライトコードの福岡本社でモバイルエンジニアやってる こー です!
2021.11.05YOUは何しにライトコードへ?〜こーくん編〜プロジェクト内で安心感を与えられる存在になりたい!今回は、弊社のエンジニアである高さんにフィーチャー!技術力に定評のあ...
前回は、GitHub Actionsを使ってAndroidアプリのバージョン番号(versionName
と versionCode
)を自動で更新し、プルリクエストを作成するワークフローについて解説しました。
▼ 前回の記事(まだの方はこちらからどうぞ!)2025.02.07【Android】GitHub Actionsでアプリのバージョン情報を更新するはじめにこんにちは!株式会社ライトコードの福岡本社でモバイルエンジニアやってる こー です!現在、個人開発しているAn...
開発中では、テスターやチームメンバーに対して動作確認用としてAPKファイルを配信することが多くあると思います。
ですが、
- Android Studioで「Build > Generate Signed Bundle / APK」を選択し、
- リリース用のAPKファイルを生成し、
- Firebaseコンソールを開き、App Distributionのページに移動し、
- 先ほど生成したAPKファイルをドラッグ&ドロップし、
- リリースノートを記入して、テスターに配布する
という一連の作業、一度や二度ならまだしも、開発が活発になればなるほど、何度も繰り返すことになります。
正直面倒くさいですし、手作業が増えれば署名設定を間違えたり、アップロードするAPKを間違えたりなどのヒューマンエラーを引き起こす可能性も高くなります。
そこで今回の後編では、前編で自動化したバージョン更新の仕組みをさらに発展させ、GitHub Actionsの画面からワンクリックするだけで、バージョンアップからAPKのビルド、そしてFirebase App Distributionへのデプロイまでを全自動で行うワークフローについて解説していきます。
それでは見ていきましょう!
今回構築するワークフローの全体像
今回目指すのは、バージョンアップの種類に応じて3つのワークフローを用意し、それぞれを手動で実行できるようにします。
Deploy To App Distribution - Major
:メジャーバージョンを上げてデプロイDeploy To App Distribution - Minor
:マイナーバージョンを上げてデプロイDeploy To App Distribution - Patch
:パッチバージョンを上げてデプロイ
これらのワークフローは、GitHubのActionsタブから「Run Workflow」ボタンを押すだけで、以下の処理をすべて自動で行ってくれます。
- バージョンの更新:
build.gradle.kts
内のversionName
とversionCode
をインクリメントします。 - リリースAPKのビルド: アプリケーションをビルドし、署名付きのリリースAPKを生成します。
- Firebase App Distributionへのデプロイ: 生成されたAPKをApp Distributionにアップロードし、テスターに配布します。
- 後処理: 次のリリースに備えて、変更内容を記録したプルリクエストを作成します。
それでは、この仕組みがどのように実現されているのか、実際のGitHub Actionsのワークフローファイルを詳しく見ていきましょう。
ワークフローファイルの解説
3つのワークフロー(Major/Minor/Patch)は、更新するバージョンの種類が違うだけで、基本的な構造はほぼ同じです。
ここでは代表として、一番利用頻度が高いであろう deploy-release-apk-patch.yml
の中身を、順次解説していきます。
1#/.github/workflows/deploy-release-apk-patch.yml
2name: Deploy To App Distribution - Patch
3
4on: [workflow_dispatch]
5
6jobs:
7 deploy-release-apk:
8 runs-on: ubuntu-latest
9 steps:
10 - uses: actions/checkout@v2
11 with:
12 ref: 'release'
13
14 - name: set up JDK 17
15 uses: actions/setup-java@v2
16 with:
17 java-version: '17'
18 distribution: 'temurin'
19 cache: gradle
20
21 - name: Grant Permission gradlew
22 run: chmod +x gradlew
23
24 - name: Make google-service.json
25 env:
26 GOOGLE_SERVICE: ${{ secrets.GOOGLE_SERVICE_JSON }}
27 run: echo $GOOGLE_SERVICE | base64 --decode --ignore-garbage > ./app/google-services.json
28
29 - name: Bump Patch Version
30 run: ./gradlew bumpPatchVersion
31
32 - name: Get Version
33 run: |
34 echo "::set-output name=VERSION_CODE::$(./gradlew -q printVersionCode)"
35 echo "::set-output name=VERSION_NAME::$(./gradlew -q printVersionName)"
36 id: version
37
38 - name: Building Release APK
39 env:
40 ENV_SIGN_KEYSTORE_BASE64: ${{ secrets.ENV_SIGN_KEYSTORE_BASE64 }}
41 ENV_SIGN_KEY_ALIAS: ${{ secrets.ENV_SIGN_KEY_ALIAS }}
42 ENV_SIGN_KEY_PASSWORD: ${{ secrets.ENV_SIGN_KEY_PASSWORD }}
43 ENV_SIGN_STORE_PASSWORD: ${{ secrets.ENV_SIGN_STORE_PASSWORD }}
44 run: ./gradlew assembleRelease
45
46 - name: Deploy to App Distribution
47 uses: wzieba/Firebase-Distribution-Github-Action@v1
48 with:
49 appId: ${{ secrets.FIREBASE_APP_ID }}
50 serviceCredentialsFileContent: ${{ secrets.CREDENTIAL_FILE_CONTENT }}
51 groups: release
52 releaseNotesFile: RELEASE_NOTE.md
53 file: ./app/build/outputs/apk/release/app-release.apk
54
55 - name: Init RELEASE_NOTE.md
56 run: cat RELEASE_NOTE.init.md > RELEASE_NOTE.md
57
58 - name: Create Pull Request
59 uses: peter-evans/create-pull-request@v6
60 with:
61 token: ${{ secrets.GITHUB_TOKEN }}
62 branch: after-released-${{ steps.version.outputs.VERSION_NAME }}-${{ steps.version.outputs.VERSION_NAME }}
63 base: release
64 title: "Released version: `${{ steps.version.outputs.VERSION_NAME }}` / `${{ steps.version.outputs.VERSION_CODE }}`"
65 commit-message: "Released version: `${{ steps.version.outputs.VERSION_NAME }}` / `${{ steps.version.outputs.VERSION_CODE }}`"
手動実行のトリガー設定
1on: [workflow_dispatch]
workflow_dispatch
を指定することで、このワークフローはGitHubのUI(Actionsタブ)から手動で実行できるようになります。
特定のブランチへのプッシュやプルリクエストをトリガーにしないことで、開発者の任意のタイミングでデプロイを実行できる柔軟性を確保しています。
ビルド & デプロイ処理
ここから実際のデプロイ処理内容について解説していきます。
jobs
セクションで deploy-release-apk
という名前のジョブを定義しています。
runs-on: ubuntu-latest
で、このジョブが最新のUbuntu環境で実行されることを指定しています。
では、steps
の中身を一つずつ見ていきましょう。
Step 1: コードのチェックアウト
1- uses: actions/checkout@v2
2 with:
3 ref: 'release'
actions/checkout
は、リポジトリのソースコードをワークフローを実行している環境(Runner)にコピーするための公式アクションです。
ここで重要なのが with: ref: 'release'
の部分です。
これは、release
ブランチの最新のコードを取得することを意味しています。
ブランチの指定ミスを防ぐために、どのブランチ上でワークフローを実行しても release
ブランチ上でデプロイ処理を行うようにしています。
Step 2: Java環境のセットアップ
1- name: set up JDK 17
2 uses: actions/setup-java@v2
3 with:
4 java-version: '17'
5 distribution: 'temurin'
6 cache: gradle
Androidアプリをビルドするためには、適切なバージョンのJava Development Kit (JDK) が必要です。actions/setup-java
アクションがその役割を担います。
java-version: '17' | このプロジェクトで必要とされているJDK 17を指定しています。 |
distribution: 'temurin' | JDKのディストリビューションとしてEclipse Temurinを指定しています。 |
cache: gradle | Gradleの依存関係やビルドキャッシュを保存します。これにより次回以降のビルド時間を大幅に短縮できます。 |
Step 3: gradlew
への実行権限付与
1- name: Grant Permission gradlew
2 run: chmod +x gradlew
gradlew
(Gradle Wrapper) は、Gradleを簡単に実行するためのスクリプトファイルです。
しかし、CI環境によっては、このファイルにデフォルトで実行権限が付与されていない場合があります。
chmod +x gradlew
は、gradlew
ファイルに実行権限 (+x
) を与えるためのLinuxコマンドです。
後のステップで ./gradlew
コマンドを実行しようとした際に「Permission denied」エラーが発生してしまう可能性があるため必要になります。
Step 4: google-services.json
の生成
1- name: Make google-services.json
2 env:
3 GOOGLE_SERVICE: ${{ secrets.GOOGLE_SERVICE_JSON }}
4 run: echo $GOOGLE_SERVICE | base64 --decode --ignore-garbage > ./app/google-services.json
Firebaseを利用するAndroidアプリには、app/google-services.json
ファイルが必須です。
このファイルにはFirebaseプロジェクトに接続するための重要な情報が含まれています。
しかし、このような セキュリティ上機密情報を含むファイルを直接リポジトリにコミットするのは厳禁ですので、GitHub Secrets を通じて取得します。
env: GOOGLE_SERVICE: ${{ secrets.GOOGLE_SERVICE_JSON }} | あらかじめリポジトリの「Settings > Secrets and variables > Actions」で登録しておいた google-services.json の内容を、secrets.GOOGLE_SERVICE_JSON を安全に呼び出し GOOGLE_SERVICE という環境変数にセットします。(JSONのままでは改行の問題で上手く取り扱えないため、 Base64 形式という1行の文字列にエンコードして登録しておきます) |
run: echo $GOOGLE_SERVICE | base64 --decode ... | 環境変数 GOOGLE_SERVICE にセットされたBase64文字列を base64 --decode で元のJSON形式にデコードし、./app/google-services.json でファイルとして書き出します。 |
これにより、機密情報を安全に管理しつつ、ワークフロー内で必要なファイルを動的に生成することができます。
Step 5: バージョンアップの実行
1- name: Bump Patch Version
2 run: ./gradlew bumpPatchVersion
run: ./gradlew bumpPatchVersion
は、前回の記事で作成したカスタムGradleタスクを実行しています。
このコマンドが実行されると、app/build.gradle.kts
内に定義されたロジックが走り、versionCode
が1つインクリメントされ、versionName
のパッチ部分が1つインクリメントされた上で、app/build.gradle.kts
ファイル自体が書き換えられます。
Step 6: バージョン情報の取得
1- name: Get Version
2 run: |
3 echo "::set-output name=VERSION_CODE::$(./gradlew -q printVersionCode)"
4 echo "::set-output name=VERSION_NAME::$(./gradlew -q printVersionName)"
5 id: version
更新後のバージョン情報を、後のステップで再利用したい場合があります(例. プルリクエストのタイトル等)。
そのため、環境変数にこちらを保存しておき、再利用できるようにしておきます。
id: version | このステップに version というIDを付与します。 |
echo "::set-output name=..." | これはGitHub Actionsの特殊な構文で、name で指定した名前(VERSION_CODE や VERSION_NAME )で、echo した内容を出力変数として設定します。 |
このステップが完了すると、後のステップで steps.version.outputs.VERSION_CODE
や steps.version.outputs.VERSION_NAME
のようにして、更新後のバージョン情報を参照できるようになります。
Step 7: リリースAPKのビルド
1- name: Building Release APK
2 env:
3 ENV_SIGN_KEYSTORE_BASE64: ${{ secrets.ENV_SIGN_KEYSTORE_BASE64 }}
4 ENV_SIGN_KEY_ALIAS: ${{ secrets.ENV_SIGN_KEY_ALIAS }}
5 ENV_SIGN_KEY_PASSWORD: ${{ secrets.ENV_SIGN_KEY_PASSWORD }}
6 ENV_SIGN_STORE_PASSWORD: ${{ secrets.ENV_SIGN_STORE_PASSWORD }}
7 run: ./gradlew assembleRelease
次はいよいよこの更新後のバージョン情報で、いよいよアプリをビルドしてリリース用のAPKを作成していきます。
./gradlew assembleRelease
がそのためのコマンドです。
しかし、Google Play StoreやApp DistributionにアップロードするAPKファイルには、デジタル署名が必須です。
この署名によって、アプリの作者が誰であるか、そしてアプリが改ざんされていないことを保証します。
署名にはキーストアファイル(.jks
)と、それに関連するパスワードやエイリアスが必要です。
これらも google-services.json
と同様に非常に重要な機密情報なので、GitHub Secretsを使って安全に取り扱っていきます。
env: ...
ブロックで署名に必要な情報をすべて環境変数として設定しています。
ENV_SIGN_KEYSTORE_BASE64 | キーストアファイル(.jks )をBase64エンコードしてSecretsに保存したもの。 |
ENV_SIGN_KEY_ALIAS | キーストアのエイリアス名。 |
ENV_SIGN_KEY_PASSWORD | キーのパスワード。 |
ENV_SIGN_STORE_PASSWORD | ストアのパスワード。 |
これらの環境変数は、app/build.gradle.kts
の signingConfigs
ブロックで読み込まれ、ビルド時に使用されます。
1signingConfigs {
2 create(releaseSigningConfigName) {
3 val releaseKeystoreFileName = "release-keystore.jks"
4 // 環境変数にキーストアのBase64文字列があればデコードしてファイルを作成
5 if (System.getenv("ENV_SIGN_KEYSTORE_BASE64") != null) {
6 System.getenv("ENV_SIGN_KEYSTORE_BASE64").let { base64 ->
7 val decoder = Base64.getMimeDecoder()
8 File(releaseKeystoreFileName).also { file ->
9 file.createNewFile()
10 file.writeBytes(decoder.decode(base64))
11 }
12 }
13 }
14 // 環境変数から署名情報を読み込む
15 storeFile = rootProject.file(releaseKeystoreFileName)
16 storePassword = System.getenv("ENV_SIGN_STORE_PASSWORD")
17 keyAlias = System.getenv("ENV_SIGN_KEY_ALIAS")
18 keyPassword = System.getenv("ENV_SIGN_KEY_PASSWORD")
19 }
20}
このように、ワークフローとビルドスクリプトが連携することで、安全かつ自動的に署名付きAPKをビルドすることができます。
Step 8: Firebase App Distributionへのデプロイ
1- name: Deploy to App Distribution
2 uses: wzieba/Firebase-Distribution-Github-Action@v1
3 with:
4 appId: ${{ secrets.FIREBASE_APP_ID }}
5 serviceCredentialsFileContent: ${{ secrets.CREDENTIAL_FILE_CONTENT }}
6 groups: release
7 releaseNotesFile: RELEASE_NOTE.md
8 file: ./app/build/outputs/apk/release/app-release.apk
最後にビルドしたAPKをFirebase App Distributionにデプロイしていきます。
ここでは wzieba/Firebase-Distribution-Github-Action
というサードパーティ製のアクションを利用しています。
これを利用することで、複雑なAPIコールなどを意識することなく、簡単な設定だけでデプロイを行うことが可能です。
with
以下のパラメータを詳しく見ていきます。
appId: ${{ secrets.FIREBASE_APP_ID }} | デプロイ対象となるFirebaseアプリのID。Firebaseプロジェクトの設定画面から確認でき、これもSecretsで管理します。 |
serviceCredentialsFileContent: ${{ secrets.CREDENTIAL_FILE_CONTENT }} | GitHub ActionsからFirebaseを操作するための認証情報(サービスアカウントの秘密鍵)。FirebaseコンソールからJSON形式でダウンロードし、その内容をSecretsに保存します。(ファイルそのものではなく、中身のテキストを保存する点に注意) |
groups: release | App Distributionにおける配布対象のテスターグループを指定。ここでは release という名前のグループに配布しています。 |
releaseNotesFile: RELEASE_NOTE.md | 配布時に表示されるリリースノートの内容をリポジトリ内の RELEASE_NOTE.md ファイルから読み込んで設定します。 |
file: ./app/build/outputs/apk/release/app-release.apk | アップロードするAPKファイルのパスを指定します。./gradlew assembleRelease で生成されるファイルの出力先をここに指定しておきます。 |
このステップが成功すれば、指定したテスターグループに新しいバージョンのアプリの配信が通知されます。
Step 9: リリースノートの初期化
1- name: Init RELEASE_NOTE.md
2 run: cat RELEASE_NOTE.init.md > RELEASE_NOTE.md
デプロイが無事に完了したら、次のリリースのために少し後片付けをします。RELEASE_NOTE.md
には今回のリリース内容が書かれていますが、このままだと次回のリリースでも同じ内容が使われてしまいます。
そこで、cat RELEASE_NOTE.init.md > RELEASE_NOTE.md
コマンドを実行します。
RELEASE_NOTE.init.md
は「定型文だけが書かれたテンプレートファイル」で、cat
コマンドでその内容を読み込み、RELEASE_NOTE.md
に上書きしています。
これにより、リリースノート用のファイルを手動でデフォルトに戻す作業を省略することができます。
Step 10: 後処理のプルリクエスト作成
1- name: Create Pull Request
2 uses: peter-evans/create-pull-request@v6
3 with:
4 token: ${{ secrets.GITHUB_TOKEN }}
5 branch: after-released-${{ steps.version.outputs.VERSION_NAME }}-${{ steps.version.outputs.VERSION_NAME }}
6 base: release
7 title: "Released version: `${{ steps.version.outputs.VERSION_NAME }}` / `${{ steps.version.outputs.VERSION_CODE }}`"
8 commit-message: "Released version: `${{ steps.version.outputs.VERSION_NAME }}` / `${{ steps.version.outputs.VERSION_CODE }}`"
このワークフロー実行による変更(build.gradle.kts
のバージョンアップと RELEASE_NOTE.md
の初期化)をマージするためのプルリクエストを作成します。
ここでは peter-evans/create-pull-request
というサードパーティ製のカスタムアクションを活用します。
token: ${{ secrets.GITHUB_TOKEN }} | アクションがリポジトリを操作(ブランチ作成やPR作成)するために必要なトークン。(GitHub側が自動的に付与)。 |
branch: after-released-... | プルリクエストの元となる新しいブランチの名前を指定します。steps.version.outputs.VERSION_NAME を使うことで、リリースしたバージョンがブランチ名から分かるようにしています。 |
base: release | release ブランチに対してプルリクエストを作成します。 |
title: "Released version: ..." | プルリクエストのタイトル。ここでも steps.version.outputs を活用して、どのバージョンがリリースされたのか一目で分かるようにしています。 |
commit-message: "Released version: ..." | コミットメッセージ。こちらもプルリクエストのタイトルと同名にしておきます。 |
このプルリクエストを release
ブランチにマージすることで、一連のデプロイ作業が完了し、その履歴がGit上に綺麗に残ることになります。
最後にこのrelease
ブランチを、メインブランチや開発用のブランチにマージし、変更を反映するようにしましょう。
これで作業は全て完了です!
さいごに
今回は、GitHub Actionsを使ってAndroidアプリの「バージョンアップ → ビルド → 署名 → Firebase App Distributionにデプロイ → 後処理」という一連の流れを、ボタン一つで完結させるワークフローを解説していきました。
自分が自動化大好き人間 + 面倒くさがり屋なので、この部分を自動化できたのはとても大きかったですね(笑)
個人開発で利用しているのですが、手作業で行うと10〜15分程かかる内容を2〜3分で完了できるようになりましたし、ヒューマンエラーもなくなるので、デプロイ作業がぐっと楽になり保守性も上がりました。
AndroidアプリはLinux環境でビルドできますし、リポジトリをPublicにしておけば、無料でこの自動化ワークフローを走らせることができるので、敷居が低く個人開発にも取り入れやすいのもメリットの大きな一つに感じています。(機密情報の漏洩には気をつける必要がありますが)
チーム開発を行う際でも、このような自動化・効率化を行うことでチーム全体の生産性を飛躍的に向上させることができます。
デフォルト環境でも比較的軽量に動くワークフローなので、もし興味があればご自身のリポジトリで体験してもらえるとめちゃめちゃ嬉しいです!
あなたの開発体験の底上げに少しでもお役に立てていれば幸いです!
最後までお読みいただき、ありがとうございました!
それではまた!
ライトコードでは、エンジニアを積極採用中!
ライトコードでは、エンジニアを積極採用しています!社長と一杯しながらお話しする機会もご用意しております。そのほかカジュアル面談等もございますので、くわしくは採用情報をご確認ください。
採用情報へ

「クレヨンしんちゃんは人生のマニュアル」が口癖なモバイルアプリとバックエンドやってる人