Github Actionsにて自動的にビルドする環境作成(Android編)

# 概要

[Github Actions](https://github.com/features/actions)でのCI/CDの検証を行いました。
その中で他の開発プロジェクトにも定常的に適用できるようなテンプレートを作成しました。
作成したテンプレートのプロジェクトは以下になります。
[github-actions-examples](https://github.com/TakuKobayashi/github-actions-examples)

今回このような環境の構築にあたっての備忘録としてここに記述しておきます。
また上記のプロジェクトは他の様々な環境において試したものであり、今回は[Github Actions](https://github.com/features/actions)における `Androidアプリ` での自動ビルドの手法についてを中心に紹介します。

Webサイトでの自動デプロイおよび[Github Actions](https://github.com/features/actions)での基本的な設定についてなどはこちらの記事を参照してください。
[Github Actionsにて自動的にデプロイする環境作成(Webサイト編)](https://qiita.com/taptappun/items/4a8f7ecaa3f91cb21be9)

またUnityでの自動ビルドの方法についてはこちらを参照してください。
[Github Actionsにて自動的にビルドする環境作成(Unity編)](https://qiita.com/taptappun/items/7c309b67e000a19c4666)

この他に `iOS` での自動ビルドの環境構築については現在検証しています。こちらも手法が確立でき次第、紹介したいと思います。

# やりたいこと

 * Androidアプリを開発し、Githubに`git push`したら自動的にビルドが行われ、成果物である [apkファイル](https://ja.wikipedia.org/wiki/APK_(%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E5%BD%A2%E5%BC%8F)) または [aabファイル](https://developer.android.com/guide/app-bundle?hl=ja) をダウンロードできること
 * Google Playストアにも公開可能なRelease用の署名鍵を[Github Actions](https://github.com/features/actions)上にて管理しその署名鍵を用いてビルドが行われ、成果物である [apkファイル](https://ja.wikipedia.org/wiki/APK_(%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E5%BD%A2%E5%BC%8F)) または [aabファイル](https://developer.android.com/guide/app-bundle?hl=ja) をダウンロードできること → 以後 `Release Build`と呼称
 * Google Play Storeにリリースするための鍵を使ってビルドが行われること ← 後日更新

# Build

## 結論

以下の内容をそれぞれ参照してください

* 開発用Buildである`Debug Build`用の[Github Actions](https://github.com/features/actions)の設定ファイル: [android-debug-build.yml](https://github.com/TakuKobayashi/github-actions-examples/blob/master/.github/workflows/android-debug-build.yml)
* Android NDK(C, C++) を用いた `Debug Build`用の[Github Actions](https://github.com/features/actions)の設定ファイル: [android-ndk-debug-build.yml](https://github.com/TakuKobayashi/github-actions-examples/blob/master/.github/workflows/android-ndk-debug-build.yml)
* `Release Build`用の[Github Actions](https://github.com/features/actions)の設定ファイル: [android-release-build.yml](https://github.com/TakuKobayashi/github-actions-examples/blob/master/.github/workflows/android-release-build.yml)
* Android NDK(C, C++) を用いた `Release Build`用の[Github Actions](https://github.com/features/actions)の設定ファイル: [android-ndk-release-build.yml](https://github.com/TakuKobayashi/github-actions-examples/blob/master/.github/workflows/android-ndk-release-build.yml)
* `build.gradle` の内容についてはこちらを参照: [app/build.gradle](https://github.com/TakuKobayashi/github-actions-examples/blob/master/Android/app/build.gradle)
* Android NDK(C, C++) を用いた `build.gradle` も同様の内容ですが、こちらを参照: [app/build.gradle](https://github.com/TakuKobayashi/github-actions-examples/blob/master/AndroidNDK/app/build.gradle)

## 各種解説

### JDKのセットアップ

AndroidアプリのビルドはJava製のアプリケーションとしてビルドします。今回はビルドするために `JDK` を使用してビルドを行います(Kotlinで開発も同様)
[Github Actions](https://github.com/features/actions)では [action/setup-java](https://github.com/actions/setup-java) を使用することで `JDK` のsetupができます。また、今回は`Java version 1.8` でビルドするので使用するバージョンを指定します。ymlファイルには以下のように記述します。

```yml
jobs:
  build:
    steps:
      ...
      - name: setup JDK 1.8
        uses: actions/setup-java@v2
        with:
          distribution: 'zulu'
          java-version: '11'
```

[action/setup-java](https://github.com/actions/setup-java) の `v2` 以降では `distribution` (JDKの配布先のサーバー) を指定する必要があるようになりました。対応している `distribution` については[こちら](https://github.com/actions/setup-java#supported-distributions) を参照

### Android SDKのセットアップ

`JDK`のセットアップができたら次は `Android SDK` をダウンロードしてセットアップします。
`Android SDK` は一般的には[Android Studio](https://developer.android.com/studio)にてダウンロードしますが、[Github Actions](https://github.com/features/actions) でBuildするためにはCLIで実行してBuildする必要があるため、[Android Studio](https://developer.android.com/studio) を使用しない方法でダウンロードします。
ダウンロード先のURLは [Android Studioのダウンロード先](https://developer.android.com/studio) にある `Command line tools only` の項目から対応したOSのものをダウンロードして使用します。[Github Actions](https://github.com/features/actions) 上でのBuildは `Linux OS` でBuildを行うため `Platform` が `Linux` のもののURLを参照して、記載します。
![commandlinetools.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72596/c87583c9-975c-949e-0449-4359916fb4d0.png)


```sh
wget --quiet --output-document=android-sdk.zip https://dl.google.com/android/repository/commandlinetools-linux-8092744_latest.zip
unzip -d android-sdk-linux android-sdk.zip
```

なお、上記の `8092744` は　`Command line tools` のバージョンの値になります。最新のものが配信されればこの値が更新されます。

`Android SDK`　のダウンロード、および解凍が完了したら、 `build-tools` と`platform-tools` などを使用するための設定や環境変数である`PATH`を通します。

```sh
echo y | android-sdk-linux/cmdline-tools/bin/sdkmanager --sdk_root=. "platforms;android-${{ matrix.compile-sdk }}" >/dev/null
echo y | android-sdk-linux/cmdline-tools/bin/sdkmanager --sdk_root=. "platform-tools" >/dev/null
echo y | android-sdk-linux/cmdline-tools/bin/sdkmanager --sdk_root=. "build-tools;${{ matrix.build-tools }}" >/dev/null
export ANDROID_SDK_ROOT=$PWD
export PATH=$PATH:$PWD/platform-tools/
```

上記で指定している値のうち
* `${{ matrix.compile-sdk }}` にはAndroidの `targetSdkVersion`(科開発を想定しているAndroid OSのバージョン番号。基本的には現在リリースされている最新のバージョンの番号に合わせる) の値を指定します。
* `${{ matrix.build-tools }}` にはAndroidアプリをBuildするためのbuild toolのバージョンの番号を指定します。

次に `Android SDK` を使用するために`lincense`を承諾します。

```sh
set +o pipefail
yes | android-sdk-linux/cmdline-tools/bin/sdkmanager --sdk_root=. --licenses
set -o pipefail
```

これで `Android SDK` の設定が完了してAndroidアプリをBuildすることができるようになります。

### gradleのセットアップ

Androidでアプリをビルドするには [Gradle](https://gradle.org/)を用います。[Gradle](https://gradle.org/) の設定は `gradlew` を実行することで自動的にセットアップしてくれます。(各種Gradleコマンドも`gradlew` を使用して実行されます)、`gradlew` ファイルを実行できるようにファイルの権限を更新します。

```sh
chmod +x ./gradlew
```

### (おまけ) Warning解消

上記までがビルドを行う設定なのですが、このままビルドを行うと `Warning` が出てしまうので `Warning` がでないようにするために途中で以下のファイルを作成します。

```sh
sudo touch /root/.android/repositories.cfg
```

### Debug Build

Androidアプリをビルドする環境が整ったら、まずは `Debug Build` を行います。
`Debug Build`は以下のコマンドを実行することでビルドすることができます。

```
./gradlew assembleDebug
```

Androidアプリをビルドするためには署名が必要なのですが、署名鍵を特に指定しない`Debug Build`では `Android SDK` がインストールされた時点で `~/.android/debug.keystore` に生成されている `debug用keystore` の署名鍵を使用してビルドが行われます。

【参考】
[debug用keystoreについて調べてみた](https://qiita.com/taka0125/items/c408df4061a0af49c9b3)

### Release Build

`Debug Build`の場合はAndroidの`debug用keystore` を用いてビルドが行われましたが、Google Play StoreなどにAndroidアプリを配布する場合、`debug用keystore` でビルドされた[apkファイル](https://ja.wikipedia.org/wiki/APK_(%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E5%BD%A2%E5%BC%8F)), [aabファイル](https://developer.android.com/guide/app-bundle?hl=ja)ではアップロードした段階でエラーが発生してしまい、配布することができません。
配布するためには署名鍵を自分で作成し、作成した署名鍵を用いてビルドされた[apkファイル](https://ja.wikipedia.org/wiki/APK_(%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E5%BD%A2%E5%BC%8F)), [aabファイル](https://developer.android.com/guide/app-bundle?hl=ja)をStoreにアップロードすることでアプリを配布することができます(ここではこのようなビルドを`Release Build`と呼称します)
以降では [Github Actions](https://github.com/features/actions) で`Release Build`を行う方法を紹介します。
また、鍵の作成については[こちら](https://developer.android.com/studio/publish/app-signing?hl=ja) などを参考に作成してください。

#### ビルドに使用する鍵を参照できるようにする

作成した署名鍵を指定してビルドをするためには署名鍵ファイルを`app/buld.gradle` にて指定することでビルドする場合に適用されます。

この時、`storeFile` `storePassword` `keyAlias` `keyPassword` の情報が必要になりますので、`app/buld.gradle` にこれらの情報を指定します。
これらの情報は秘匿して管理したい環境変数でもあるので、Androidにて環境変数を記述するために使われるファイルである `local.properties` に以上の情報を記述し、その内容を`app/build.gradle` 内にて読み込ことでビルドを行うときに参照するようにします。(`local.properties` はデフォルトで`.gitignore` に記載されているので更新しても変更は反映されません)

`app/buld.gradle` への記述は以下のようになります。

```build.gradle
Properties properties = new Properties()
def localPropertiesFile = project.rootProject.file('local.properties');
if(!localPropertiesFile.exists()){
    // localPropertiesFileがなければ作成します
    localPropertiesFile.createNewFile();
}
properties.load(localPropertiesFile.newDataInputStream())
```

上記の例では ビルド時に `local.properties` がなければ、新しく作成し、あれば既存のファイルを読み込むことで設定に適用するような処理になるように記述しています。(`local.properties` がない場合(`Debug Build`を行う場合などにおいて)、ビルドエラーになってしまうため)

読み込んだ、`local.properties` の値をビルドを`Release Build` の際に適用するためには以下のように記述します。

```build.gradle
android {
    ...
    signingConfigs {
        release {
            // 指定したfileがある場合のみ記述
            if(!properties.getProperty("RELEASE_STORE_FILE", "").empty){
                storeFile file(properties.getProperty("RELEASE_STORE_FILE", ""))
            }
            storePassword properties.getProperty("RELEASE_STORE_PASSWORD", "")
            keyAlias properties.getProperty("RELEASE_KEY_ALIAS", "")
            keyPassword properties.getProperty("RELEASE_KEY_PASSWORD", "")
        }
    }
    buildTypes {
        release {
            signingConfig signingConfigs.release
        }
    }
}
```

このとき `local.properties` には以下のような環境変数を設定します。

```local.properties
RELEASE_STORE_FILE=署名鍵ファイルのファイルパス(絶対パス)文字列
RELEASE_STORE_PASSWORD=ストアパスワード文字列
RELEASE_KEY_ALIAS=キーエイリアス文字列
RELEASE_KEY_PASSWORD=キーパスワード文字列
```

※ 上記の設定を適用した場合、ローカルで `Release Build` を行う場合は `local.properties` に以上の内容を記述し、署名鍵ファイルの情報を記述してください。

#### 鍵の管理

本来なら署名鍵ファイルはバイナリファイルなのでどこかに保存して取得するかプロジェクトの中に埋め込んで管理します。
しかしそれだと多くの人に署名鍵の情報が知れ渡ることとなり、情報漏洩や鍵の紛失、管理の観点からリスクが大きいものとなってしまいます。

<font color="#FF0000">そこで今回、署名鍵の管理をGithub Secretsに保存し、秘密の環境変数として適用します</font>

署名鍵のファイルサイズは軽微なものであるので、[base64](https://ja.wikipedia.org/wiki/Base64) でバイナリファイルを文字列化します。
この文字列を `Github Secrets` に保存し、[Github Actions](https://github.com/features/actions) を実行した時にバイナリファイルに戻して保存することで秘匿されたまま鍵の管理が可能となります。まず署名鍵ファイルを[base64](https://ja.wikipedia.org/wiki/Base64)で文字列に出力します。

```sh
base64 Androidのビルド署名鍵.keystore
```

このコマンドを実行して出力された文字列を、`Github Secrets` に登録します。

[Github Actions](https://github.com/features/actions) 内ではここで設定した `Github Secrets` の情報が適用されるので、以下のように`base64 -d >`でバイナリファイルに書き出します。

```sh
echo ${ANDROID_REALSE_BASE64_KEY} | base64 -d > ./release-application-key
```

※ ここでは `ANDROID_REALSE_BASE64_KEY` にバイナリファイルを [base64](https://ja.wikipedia.org/wiki/Base64) にて文字列にした物を `Github Secrets` に設定しています。
`release-application-key` は一時的に保存する署名鍵のファイル名となります。

#### `local.properties` に上記の各種`Release Build`を行うために必要な情報を記述する

[Github Actions](https://github.com/features/actions) を行う場合、`Release Build`を行うために必要な情報を `local.properties` に以下の情報を記述します。

```local.properties
RELEASE_STORE_FILE=署名鍵ファイルのファイルパス(絶対パス)文字列
RELEASE_STORE_PASSWORD=ストアパスワード文字列
RELEASE_KEY_ALIAS=キーエイリアス文字列
RELEASE_KEY_PASSWORD=キーパスワード文字列
```

これらの情報は全てGithub Secretsに設定し、設定した内容を以下のように `local.properties` に追記していくようにします。

```sh
echo ${ANDROID_REALSE_BASE64_KEY} | base64 -d > ./release-application-key
echo "RELEASE_STORE_FILE=`pwd`/release-application-key" >> ./local.properties
echo "RELEASE_STORE_PASSWORD=${ANDROID_REALSE_KEY_PASSWORD}" >> ./local.properties
echo "RELEASE_KEY_ALIAS=key0" >> ./local.properties
echo "RELEASE_KEY_PASSWORD=${ANDROID_REALSE_KEY_PASSWORD}" >> ./local.properties
```

これにより [Github Actions](https://github.com/features/actions) にて実行される際に `local.properties` に値が適用された状態でビルドが行われます。

#### Gradleで`Release Build`を行う

以下のコマンドを実行することで [apkファイル](https://ja.wikipedia.org/wiki/APK_(%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E5%BD%A2%E5%BC%8F)) を出力する`Release Build`を行います

```sh
./gradlew assembleRelease
```

続けて、以下のコマンドを実行することで [aabファイル](https://developer.android.com/guide/app-bundle?hl=ja) を出力する`Release Build`を行います

```sh
./gradlew bundleRelease
```

### ビルドした成果物をダウンロードできるようにする

`Debug Build`, `Release Build`共にビルドを実行完了した後の[apkファイル](https://ja.wikipedia.org/wiki/APK_(%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E5%BD%A2%E5%BC%8F)), [aabファイル](https://developer.android.com/guide/app-bundle?hl=ja)などの各種成果物をダウンロードできるようにには以下の項目を追加します

```yml
- uses: actions/upload-artifact@v3
  with:
    name: outputs
    path: app/build/outputs/
```

これにより以下のように成果物をダウンロードすることができます。

![release-build-artifact.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72596/dca90a4b-3641-5edf-fe8c-f73bd24e3342.png)

[apkファイル](https://ja.wikipedia.org/wiki/APK_(%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E5%BD%A2%E5%BC%8F))の成果物は `outputs/apk/release/app-release.apk`, [aabファイル](https://developer.android.com/guide/app-bundle?hl=ja)の成果物は `outputs/bundle/release/app-release.aab` にあります。

### [Github Actions](https://github.com/features/actions)のファイル保存容量の制限について

上記のように [Github Actions](https://github.com/features/actions) を用いて [apkファイル](https://ja.wikipedia.org/wiki/APK_(%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E5%BD%A2%E5%BC%8F)), [aabファイル](https://developer.android.com/guide/app-bundle?hl=ja)のBuildを繰り返していくと、途中でエラーが発生するようになります。
これは無料プランでGitHubを利用している場合は，500MB/月までの容量までしか保存できないためです。
Githubのプランを変更するか[Firebase App Distribution](https://firebase.google.com/docs/app-distribution?hl=ja)などのアプリ配信サービスと連携することでエラーが発生しないようにする必要があります。(やり方などの解説は後日配信予定)

#### 参考

* [GitHub Actionsのストレージ容量](https://pc.atsuhiro-me.net/entry/2021/02/05/095605)

### Android NDK(C, C++) を用いたBuildの設定方法

ここまでのBuild設定の話は全て Android NDK(C, C++) を使用しない場合の話になります。Android NDK(C, C++)を使用する場合に追加で設定する必要があります。

#### Android NDKをダウンロードする

上記にてダウンロードした `Android SDK` の中にある `sdkmanager` を使用して`Android NDK` とBuildに使う `CMake`(`CMake` を使ったBuildを行う場合)を以下のようにダウンロードします。

```sh
android-sdk-linux/cmdline-tools/bin/sdkmanager --sdk_root=. --install "ndk;${{ matrix.ndk-version }}" "cmake;${{ matrix.cmake-version }}"
```

またこの時に指定する `Android NDK` と `CMake` のバージョンについて以下のように `Android Studio` にて使用しているバージョンの情報を参考にして指定します

![AndroidSDKInstallList.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72596/7e6fea80-1020-0a97-edf3-34ef7cb3c4e8.png)

#### ダウンロードした`Android NDK`にパスを通して使用できるようにする

使用する `Android NDK` の場所を指定するには `local.properties` の `ndk.dir=` にパスを指定することでBuildする時に参照してくれるので、以下のようにその値を挿入します。

```sh
echo "ndk.dir=$PWD/ndk/${{ matrix.ndk-version }}" >> ./local.properties
```

この `Android NDK` のパスの指定は普段は `Android Studio` 内で自動的に設定してくれています。今回はこの情報を手動で設定しています。

#### 参考

* [NDK と CMake のインストールと設定](https://developer.android.com/studio/projects/install-ndk)

# まとめ

以上が [Github Actions](https://github.com/features/actions) でAndroidのネイティブアプリを自動的にビルドするための方法を紹介になります。
いち早くプロジェクトに導入したい場合は以下の内容をそれぞれ参照(コピー)して導入してください

* [android-debug-build.yml](https://github.com/TakuKobayashi/github-actions-examples/blob/master/.github/workflows/android-debug-build.yml)
* [android-ndk-debug-build.yml](https://github.com/TakuKobayashi/github-actions-examples/blob/master/.github/workflows/android-ndk-debug-build.yml)
* [android-release-build.yml](https://github.com/TakuKobayashi/github-actions-examples/blob/master/.github/workflows/android-release-build.yml)
* [android-ndk-release-build.yml](https://github.com/TakuKobayashi/github-actions-examples/blob/master/.github/workflows/android-ndk-release-build.yml)
* [Android/app/build.gradle](https://github.com/TakuKobayashi/github-actions-examples/blob/master/Android/app/build.gradle)
* [AndroidNDK/app/build.gradle](https://github.com/TakuKobayashi/github-actions-examples/blob/master/AndroidNDK/app/build.gradle)

# 今後の展望
 * Google Play Storeにリリースする方法について
 * Android のテストを実行する方法について

を追加したいと思います。