Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
21
Help us understand the problem. What are the problem?
@tarumzu

GitHub Actionsを使ってAndroidアプリをFirebase App Distributionへ配信するまでのベストプラクティス

はじめに

どうも、GitHub Actions大好きな@tarumzuです。
今回はGitHub ActionsでAndroidアプリを配信する際のベストプラクティスな記事がなかなか無いなと思ったので、無いなら作ろう!という試みです。
例としてdev環境とstg環境の2つをApp Distributionで配信するという流れをGitHub Actionsで作ります。

コード

早速ですがコードの全文です。今回はタスク毎にJobを分けて対応しています。こちらのコードを順を追って説明していきます。

.github/workflows/publish.yml
name: publish
on:
  push:
    branches: # 今回はdevelopブランチをdev環境、release/〇〇〇ブランチをstg環境への配信としてます
      - 'develop'
      - 'release/**'
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: cache gradle
        uses: actions/cache@v2
        id: cache_gradle
        with:
          path: |
            ~/.gradle/caches
            ~/.gradle/wrapper
          key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}
          restore-keys: |
            ${{ runner.os }}-gradle-
        continue-on-error: true
      - name: set up JDK 1.8
        uses: actions/setup-java@v1
        with:
          java-version: 1.8
      - name: restore debug.keystore
        run: |
          echo ${{ secrets.DEBUG_KEYSTORE }} | base64 --decode > ~/signingConfigs/debug.keystore
        shell: bash
      - name: Build with Gradle [DEV]
        if: github.ref == 'refs/heads/develop'
        run: ./gradlew :app:assembleDevDebug
      - name: Build with Gradle [STG]
        if: github.ref != 'refs/heads/develop'
        run: ./gradlew :app:assembleStgDebug
      - name: Upload apk
        uses: actions/upload-artifact@v2
        with:
          name: apk
          path: app/build/outputs/apk
          retention-days: 14
  publish:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Download apk
        uses: actions/download-artifact@v2
        with:
          name: apk
      - name: Publish Firebase App Distribution [DEV]
        if: github.ref == 'refs/heads/develop'
        uses: wzieba/Firebase-Distribution-Github-Action@v1
        with:
          appId: ${{secrets.FIREBASE_DEV_APP_ID}}
          token: ${{secrets.FIREBASE_TOKEN}}
          file: dev/debug/app-dev-debug.apk
      - name: Publish Firebase App Distribution [STG]
        if: github.ref != 'refs/heads/develop'
        uses: wzieba/Firebase-Distribution-Github-Action@v1
        with:
          appId: ${{secrets.FIREBASE_STG_APP_ID}}
          token: ${{secrets.FIREBASE_TOKEN}}
          groups: testers
          file: stg/debug/app-stg-debug.apk
  notice:
    if: always()
    needs: publish
    runs-on: ubuntu-latest
    steps:
      - uses: technote-space/workflow-conclusion-action@v1
      - uses: 8398a7/action-slack@v3
        with:
          status: custom
          fields: workflow,job,commit,repo,ref,author,took
          custom_payload: |
            {
              username: 'action-slack',
              icon_emoji: ':octocat:',
              attachments: [{
                color: '${{ env.WORKFLOW_CONCLUSION }}' === 'success' ? 'good' : '${{ job.status }}' === 'failure' ? 'danger' : 'warning',
                text: `${process.env.AS_WORKFLOW}\n${process.env.AS_JOB} (${process.env.AS_COMMIT}) of ${process.env.AS_REPO}@${process.env.AS_REF} by ${process.env.AS_AUTHOR} succeeded`,
              }]
            }
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # optional
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} # required

buildジョブ

caches

まずはコードのビルドを担当するbuildジョブです。
最初のstepで大事なのがactions/cache@v2 1 です。今回はビルドキャッシュである ~/.gradle/caches~/.gradle/wrapper の2つをキャッシュしてCI実行時間の削減をしてます。この2つで数百MBあるので結構大事。

  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2 # GitHubのcheckout
      - name: cache gradle
        uses: actions/cache@v2
        id: cache_gradle
        with:
          path: |
            ~/.gradle/caches
            ~/.gradle/wrapper
          key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}
          restore-keys: |
            ${{ runner.os }}-gradle-
        continue-on-error: true

keystoreのリストア

続いて大事なのがkeystoreファイルのリストアです。GitHub ActionsではファイルをSecrets 2 として指定できないためbase64でstringにして保存します。

keystoreをbase64エンコードする方法
base64 ~/signingConfigs/debug.keystore

base64エンコードされた文字列をデコードする場合は下記のようにしてください。

      - name: set up JDK 1.8
        uses: actions/setup-java@v1
        with:
          java-version: 1.8
      - name: restore debug.keystore
        run: |
          echo ${{ secrets.DEBUG_KEYSTORE }} | base64 --decode > ~/signingConfigs/debug.keystore
        shell: bash

build

ここまで来たらようやくbuildです。
if文を使ってdev環境とstg環境でビルドコマンドを分けています。コマンドは皆様の環境のBuild Variantsによって異なるのでよしなに置き換えてください。

      - name: Build with Gradle [DEV]
        if: github.ref == 'refs/heads/develop'
        run: ./gradlew :app:assembleDevDebug
      - name: Build with Gradle [STG]
        if: github.ref != 'refs/heads/develop'
        run: ./gradlew :app:assembleStgDebug

ジョブ間でapkファイルを受け渡しするためにアップロードする

ビルドが完了したらジョブ間でファイルを受け渡しするための設定を行いましょう。これをやらないとせっかく作成したAPKが台無しになってしまいます。
ここでは app/build/outputs/apk 以下のファイルを他のジョブに渡したいのでこのpathにあるディレクトリをアーティファクトとしてアップロードします。

      - name: Upload apk
        uses: actions/upload-artifact@v2
        with:
          name: apk
          path: app/build/outputs/apk
          retention-days: 14

※ 無料枠の場合、ストレージには制限がありアップロード上限が500MBまでとなっています。今回はアーティファクトを定期削除するためにretention-daysで14日で削除するように設定しています。

publishジョブ

needs

needsはジョブの依存関係が定義できます。下記の例だとbuildジョブのあとに実行するという意味になります。

  publish:
    needs: build

別のジョブでアップロードされたapkファイルをダウンロードする

ここで先程のbuildジョブでアップロードしたapkをダウンロードします。ファイル名はapkと定義しているのでここでは apk を指定するだけです。

    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Download apk
        uses: actions/download-artifact@v2
        with:
          name: apk

続いて今回のゴールであるApp Distributionでの配信です。ここもdev環境、stg環境でsecrets変数を分けたかったのでif文を用いてます。今回は wzieba/Firebase-Distribution-Github-Action@v1というアクションを利用させてもらってます。

      - name: Publish Firebase App Distribution [DEV]
        if: github.ref == 'refs/heads/develop'
        uses: wzieba/Firebase-Distribution-Github-Action@v1
        with:
          appId: ${{secrets.FIREBASE_DEV_APP_ID}}
          token: ${{secrets.FIREBASE_TOKEN}}
          file: dev/debug/app-dev-debug.apk
      - name: Publish Firebase App Distribution [STG]
        if: github.ref != 'refs/heads/develop'
        uses: wzieba/Firebase-Distribution-Github-Action@v1
        with:
          appId: ${{secrets.FIREBASE_STG_APP_ID}}
          token: ${{secrets.FIREBASE_TOKEN}}
          groups: testers
          file: stg/debug/app-stg-debug.apk

noticeジョブ

配信できたら通知がほしい!ということで最後にslack通知です。通知には 8398a7/action-slack@v3を利用しています。また、ジョブを複数に分けると各ジョブの結果を受け取るのが難しいのですが、そこは technote-space/workflow-conclusion-action@v1アクションを使うことですべてのジョブ実行結果のサマリーがenv.WORKFLOW_CONCLUSIONに保存されるようになるのでこちらを利用することで解決してます。

  notice:
    if: always() # 他のジョブが成功しても失敗しても実行させる
    needs: publish # publishジョブのあとに実行
    runs-on: ubuntu-latest
    steps:
      - uses: technote-space/workflow-conclusion-action@v1 # 他のジョブの成功、失敗を検知するために必要!
      - uses: 8398a7/action-slack@v3
        with:
          status: custom
          fields: workflow,job,commit,repo,ref,author,took
          custom_payload: |
            {
              username: 'action-slack',
              icon_emoji: ':octocat:',
              attachments: [{
                color: '${{ env.WORKFLOW_CONCLUSION }}' === 'success' ? 'good' : '${{ job.status }}' === 'failure' ? 'danger' : 'warning',
                text: `${process.env.AS_WORKFLOW}\n${process.env.AS_JOB} (${process.env.AS_COMMIT}) of ${process.env.AS_REPO}@${process.env.AS_REF} by ${process.env.AS_AUTHOR} succeeded`,
              }]
            }
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # optional
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} # required

最後に

最近よくGitHub Actionsを利用しているのですが、まだまだ利用しているAndroidエンジニアが少ないようで、ネット上での情報が不足していて対応に難儀することが多々あります。
この記事を機に利用してくれる人が増えてくれると嬉しいです!

参考


  1. actions/cache@v2はキャッシュがあればキャッシュを復元します。また、ジョブの最後でここのpathで指定しているファイルをキャッシュしてくれます。 

  2. GitHubリポジトリ -> Settings -> Secretsで文字列を変数として扱うことができます。 

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
21
Help us understand the problem. What are the problem?