はじめに
どうも、GitHub Actions大好きな@tarumzuです。
今回はGitHub ActionsでAndroidアプリを配信する際のベストプラクティスな記事がなかなか無いなと思ったので、無いなら作ろう!という試みです。
例としてdev環境とstg環境の2つをApp Distributionで配信するという流れをGitHub Actionsで作ります。
コード
早速ですがコードの全文です。今回はタスク毎にJobを分けて対応しています。こちらのコードを順を追って説明していきます。
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にして保存します。
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エンジニアが少ないようで、ネット上での情報が不足していて対応に難儀することが多々あります。
この記事を機に利用してくれる人が増えてくれると嬉しいです!
参考