FlutterアプリをGitHub Actionsを使用してFirebase App Distributionにデプロイしてみよう！

どうも！okeです！

**「Gitでプッシュした時に上司にアプリをすぐ検証していただきたきたい…！！」**

という社畜脳によってCI/CDなるものを学習したので知識を共有します！

最近Flutterにハマっており、FlutterデフォルトアプリをFirebase App Distributionで配布して上司に見てもらえるまでの手順を解説していきます！
一つ一つ手順を追って説明しようとしたため、かなりボリューミーになってしまいました。。

手順通りに手を動かしていただくと、恐らくFirebase App Distributionへのデプロイまで行えるようになるのではないかと思っております。
(もしうまくできない方がいらっしゃいましたらコメントしていただければ幸いです :bow_tone1: )

また、Firebaseの構成ファイルも使用する機会が個人的に多くなりそうなので、
今回は構成ファイルがあるパターンで設定していきます。

今回のソースは[こちらのリポジトリ](https://github.com/oke331/flutter_cicd)に上げております！適宜ご参照ください！

主にCI/CDに関わる箇所をメインに記載していきます。（他はちょっと端折ります！すいません！）

それではいきましょう！！

# 流れ

1. Flutterデフォルトアプリ作成
2. Gitの設定
3. Firebaseにアプリを登録
4. Firebase App Distributionの設定
5. （詳しく追います）GitHub ActionsでFirebase App DistributionにAndroidアプリをデプロイ
6. （詳しく追います）GitHub ActionsとfastlaneでFirebase App DistributionにiOSアプリをデプロイ

# 使用するツール

- Flutter
- Firebase(App Distribution)
- GitHub Actions
- fastlane
- 筆者はmacOSを使用しています。

#1. Flutter デフォルトアプリ作成

※Flutterをまだ触ったことが無い方は[公式ページ](https://flutter.dev/docs/get-started/install)を一度ご覧いただければと思います。

任意のフォルダで下記コマンドを実行！

```bash
flutter create --org com.your.orgname flutter_cicd
```

「—org」をつけて任意の文字を指定すると、iOSのBundle IdentifierとAndroidのパッケージ名を変更することができます！

[com.your.orgname]の箇所は適当な文字に置き換えてください！

（何も指定せずにcreateすると、「com.examle」の文字が付き、後々iOSのApp IDの登録のところで重複エラーが返ってきてしまう可能性があります。。僕はエラーが返ってきてやり直しました。。）

「flutter_cicd」のflutterプロジェクトが作成されていればOK！

#2. Gitの設定

### GitHubにリポジトリの作成

※GItHubをまだ触ったことのない方は[公式ページ](https://docs.github.com/ja/free-pro-team@latest/github/getting-started-with-github)をご覧ください。

[GitHub](https://github.co.jp/)にリモートリポジトリを作成してください。

今回は「flutter_cicd」という名前で作成しました！

### FlutterアプリのGitリポジトリ作成

※Gitを触ったことのない方は[サル先生のGit入門のサイト](https://backlog.com/ja/git-tutorial/) をさっと見ることをおすすめします！

先ほど作成したFlutterデフォルトアプリのルートディレクトリで、
下記コマンドを打ってGitリポジトリを作成・プッシュしましょう！

```bash
git init  # gitリポジトリの初期化
git add .
git commit -m "first commit"
git branch -M main  # mainブランチにリネーム
git remote add origin https://github.com/{ユーザー名}/flutter_cicd.git  # リモートリポジトリをoriginという名前で追加
git push -u origin main # リモートリポジトリのmainブランチにプッシュ
```

#3. Firebaseにアプリを登録

※Firebaseをまだ触ったことが無い方は、Firebase公式ページの[Android](https://firebase.google.com/docs/android/setup?authuser=0#console) / [iOS](https://firebase.google.com/docs/ios/setup?authuser=0#create-firebase-project) のプロジェクト追加手順をご覧いただければと思います。

Firebaseにアプリを登録します。

簡単な流れを以下に記します！

1. [Firebaseのwebサイト](https://firebase.google.com/docs/ios/setup?hl=ja)にアクセス
2. コンソールへ移動
3. 任意の名前のプロジェクトを追加
4. アプリを追加 　プラットフォーム毎に下記設定を行う
Android：作成したflutterアプリのパッケージ名を記入し、google-service.jsonを任意のフォルダにダウンロード（他はスキップでOK！）
iOS：作成したflutterアプリのバンドルIを記入し、GoogleService-Info.plistを任意のフォルダにダウンロード（他はスキップでOK！）

5. ダウンロードしたファイルを下記フォルダに格納
Android：android/app/google-service.json
iOS：ios/Runner/GoogleService-Info.plist

6. 後ほど作成するGithubのリモートリポジトリにFirebase構成情報をアップロードしないために、プロジェクトルートの.gitignoreに以下2つを追記

```bash:.gitignore
google-services.json
GoogleService-Info.plist
```

#4. Firebase App Distributionの設定

FirebaseのApp Distributionとは、上司（テスターの方々）にアプリを容易に配布可能なサービスです。(2020年12月9日現在 Beta版となっています)

予め上司のメールアドレスを登録しておき、
Androidは「apk」ファイル、iOSは「ipa」ファイルをApp Distributionにアップロードすると、
メールで上司に届き、すぐにアプリを検証していただくことができます！

では、App Distributionの設定をささっと行っておきましょう！
（Firebaseがパッと見てわかりやすいUIなので大丈夫と思うのですが、手順だけ示しておきます！）

1. Firebaseコンソールから作成したプロジェクトを選択
2. サイドバーからApp Distributionを選択
3. アプリを選択し、開始ボタンをポチッ！（AndroidとiOS両方ポチッ！とします）


そして、テスターとグループに上司を追加しておきましょう！

1. 「テスターを追加する」を選択し、上司のメールアドレスを追加
2. グループを追加で適当なグループを作成（今回は「company」という名前にします）
3. 作成したグループを選択し「テスターを追加する」で上司を追加

以上でApp Distributionの準備は完了です！

# 5. GitHub ActionsからFirebase App DistributionにAndroidアプリをデプロイ

ここからメインディッシュです！

一つ一つ手順を追っていきましょう！

### GitHub Actionsのファイル作成

※詳細な内容は[公式](https://docs.github.com/ja/free-pro-team@latest/actions/learn-github-actions)をご覧ください。

Github Actionsは、

{プロジェクトルート}/.github/workflows/〇〇.yaml

のファイルを読み取り、その中に組まれているコマンドを実行してくれます！

実行してくれるタイミングや、実行する内容はyamlファイルに記入していきます。
「.github」「workflows」フォルダを作成し、任意の名前でAndroid用のyamlファイルを作成しましょう！
今回は「android_cicd」という名前で作っていきます！

### Androidのyamlファイルを作成！

それでは、android_cicd.yamlの中身を見てみましょう！

下で詳しく解説します！

```yaml:.github/workflows/android_cicd.yaml
name: android_cicd

on:
  push:
    branches:
      - main

jobs:
  build:

    runs-on: ubuntu-latest
    timeout-minutes: 20

    steps:
      - uses: actions/checkout@v2

      - name:  Translate secrets of base64 into json
        env:
            GOOGLE_SERVICE: ${{ secrets.ANDROID_GOOGLE_SERVICE }}
        run:
            echo $GOOGLE_SERVICE | base64 --decode > ./android/app/google-services.json

      - uses: subosito/flutter-action@v1.4.0
        with:
          channel: 'stable'

      - name: flutter pub get
        run: flutter pub get

      - name: flutter test
        run: flutter test

      - name: Create key.properties
        run: |
          echo ${{ secrets.ANDROID_KEY_JKS }} | base64 -d > android/release.jks
          echo 'storeFile=release.jks' > android/key.properties
          echo 'storePassword=${{ secrets.ANDROID_STORE_PASSWORD }}' >> android/key.properties
          echo 'keyPassword=${{ secrets.ANDROID_KEY_PASSWORD }}' >> android/key.properties
          echo 'keyAlias=${{ secrets.ANDROID_KEY_ALIAS }}' >> android/key.properties

      - name: Build APK
        run: flutter build apk --release --build-number=$GITHUB_RUN_NUMBER

      - name: Upload apk to Firebase App Distribution
        uses: wzieba/Firebase-Distribution-Github-Action@v1
        with:
          appId: ${{secrets.FIREBASE_ANDROID_APP_ID}}
          token: ${{secrets.FIREBASE_APP_TOKEN}}
          groups: company
          file: ./build/app/outputs/apk/release/app-release.apk
```

それでは解説していきます！

---

```yaml
name: android_cicd
```

GitHub リポジトリの [Actions] タブに表示されるワークフローの名前です。
任意の名前をつけてください。

---

```yaml
on:
  push:
    branches:
      - main
```

どのタイミングで実行するかを決定します。
今回はmainブランチにプッシュされたタイミングで実行するようにします。

---

```yaml
jobs:
  build:
```

ワークフローファイルで実行されるすべてのジョブをグループ化します。

このジョブの中に定義したOSでコマンドを実行していくことになります。
今回はジョブを1つ作成し、「build」という名前で定義しています。

---

```yaml
runs-on: ubuntu-latest
```

どのマシン上で実行するか定義します。詳細は[こちら](https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions#jobsjob_idruns-on)。

同じ時間実行するにしても、macOSだとubuntuの10倍の時間を消費したとみなされる等の規定があるため、ジョブの実行時間に上限のあるプランの場合、注意してください！

---

```yaml
timeout-minutes: 20
```

タイムアウトを20分に設定しています。

僕はこのタイムアウトを設定せずに、
Action実行後、のんきにお風呂に入ってご飯食べて戻ってきたら無料枠を使い切っていたことがありました。
絶対に設定しておきましょう（泣）

---

```yaml
 steps:
```

ジョブで実行されるすべてのステップをグループ化します。

この下にネストされた各アクション（コマンド）を実行していくようになります。

---

```yaml
- uses: actions/checkout@v2
```

usesでGitHubで公開されているアクションを使用することができます。
今回はmainブランチをチェックアウトしてランナー（ubuntu）にダウンロードし、コードに対してアクションを実行できるようにします。

---

```yaml
 - name:  Translate secrets of base64 into json
        env:
            GOOGLE_SERVICE: ${{ secrets.ANDROID_GOOGLE_SERVICE }}
        run:
            echo $GOOGLE_SERVICE | base64 --decode > ./android/app/google-services.json
```

 google-service.jsonファイルをリモートリポジトリにアップロードしないため、base64で変換したものをGitHubに秘密の文字列としてアップロードしておき、アクションでbase64をデコードして読み込むようにします。

GitHubには他の人に見せたくない秘密なものを格納することができるSecretsというものが用意されており、そこに格納したものを${{ secrets.〇〇 }} で取得することができます。

Secretsに値を格納するには、
GitHubのflutter_cicdのリポジトリのURLにブラウザでアクセスし、

Settingsタブ > Secrets > New repository secret

を開きます！

この場合、キーは**「ANDROID_GOOGLE_SERVICE」**で、
バリューには下記コマンドで作成した値を設定します。

google-services.jsonがあるフォルダで下記を実行します！

```yaml
cat google-services.json | base64
```

表示された文字をバリューに設定します！

---

```yaml
   - uses: subosito/flutter-action@v1.4.0
        with:
          channel: 'stable'
```

Flutterコマンドを使用するため、こちらを記載します！

---

```yaml
  - name: Create key.properties
        run: |
          echo ${{ secrets.ANDROID_KEY_JKS }} | base64 -d > android/release.jks
          echo 'storeFile=release.jks' > android/key.properties
          echo 'storePassword=${{ secrets.ANDROID_STORE_PASSWORD }}' >> android/key.properties
          echo 'keyPassword=${{ secrets.ANDROID_KEY_PASSWORD }}' >> android/key.properties
          echo 'keyAlias=${{ secrets.ANDROID_KEY_ALIAS }}' >> android/key.properties
```

Androidでリリースビルドを行うために、key.propertiesファイルを作成し、そこに鍵の情報を記載していきます。

keyAlias（エイリアス）とはアプリの署名時に使用する秘密鍵の名前です。任意の名称をつけてください！
storeFile（キーストア）とは、単数または複数の秘密鍵と証明書のセットを格納するために使用します。

イメージとしては、金庫（storeFile）の中に秘密鍵（keyAlias）が入っており、
それぞれにパスワードが必要となっている、といった形です！

下記コマンドを実行すると鍵を作成できるのですが、いろいろな情報を入力するように求められます。
[このサイト](https://phpjavascriptroom.com/?t=mobile&p=android_keystore)が参考になります。

※エイリアス名とパスワードは忘れないようにメモしておきましょう！
※alias_nameは任意の名前に置き換えてください。

```bash
keytool -genkey -v -keystore release.jks -alias [alias_name] -keyalg RSA -keysize 2048 -validity 36500
```

上記コマンドを実行すると、release.jksファイルが作成されますが、こちらはGitRepositoryに上げないよう注意しましょう！（.gitignoreに含めておきましょう）

先ほどのgoogle-servicesと同様、base64にします！

※僕の環境だと、最後に「%」がついていたのですがそちらを削除して登録しました。

```bash
openssl base64 -A -in release.jks
# -Aで1行に表示
# -inでファイルを読み込み
```

では、GitHubのSecretsを開いて、下記情報を入力していきましょう！

- ANDROID_KEY_JKS：base64でエンコードした文字列
- ANDROID_STORE_PASSWORD：設定したパスワード
- ANDROID_KEY_PASSWORD：設定したパスワード
- ANDROID_KEY_ALIAS：設定したエイリアスの名称

アクションでは、上記のSecretsの情報をandroid/key.propertiesファイルを作成し、追記しているだけです。
では、そのkey.propertiesはどうやって読み込むのでしょう？

android/app/build.gradleに記載して読み込む必要があります！
記載する内容は下記の通りですー！

```diff_gradle:android/app/build.gradle
// 追記：key.propertiesファイルの読み込み
+ def keystorePropertiesFile = rootProject.file("key.properties")
+ def keystoreProperties = new Properties()
+ keystoreProperties.load(new FileInputStream(keystorePropertiesFile))


// 「android{ }」の中に以下の内容を追記・使用
android {

+   signingConfigs {
+      release {
+          keyAlias keystoreProperties['keyAlias']
+          keyPassword keystoreProperties['keyPassword']
+          storeFile rootProject.file(keystoreProperties['storeFile'])
+          storePassword keystoreProperties['storePassword']
+      }
+   }

+   buildTypes {
+       release {
+           signingConfig signingConfigs.release
+       }
+   }
}
```

これで、GitHub Actionsでのリリースビルド（buildTypes.release）のときに、先ほど設定した鍵情報（signingConfigs.release）を使用してくれるようになりました！

※ローカル環境でリリースビルドする場合は、key.propertiesを作成してください！

---

```yaml
  - name: flutter pub get
    run: flutter pub get

  - name: flutter test
    run: flutter test 
```

flutterのパッケージを導入し、テストを走らせます！！

---

```yaml
      - name: Build APK
        run: flutter build apk --release --build-number=$GITHUB_RUN_NUMBER
```

flutterコマンドでapkを作成します！

$GITHUB_RUN_NUMBERとはリポジトリ内のワークフロー毎にユニークに採番され、1からスタートし実行される度にインクリメントされる番号です！

---

```yaml
      - name: upload apk to Firebase App Distribution
        uses: wzieba/Firebase-Distribution-Github-Action@v1
        with:
          appId: ${{secrets.FIREBASE_ANDROID_APP_ID}}
          token: ${{secrets.FIREBASE_APP_TOKEN}}
          groups: company
          file: ./build/app/outputs/apk/release/app-release.apk
```

こちらで最後ですー！！

このちょっとの記載でFirebaseAppDistributionにアップロードできます！

groupsには、App Distributionで設定した「company」グループを指定します。

fileには先ほどflutterコマンドで作成したapkファイルを指定します。

今回もSecretsを使用します。

- FIREBASE_ANDROID_APP_ID：FirebaseのアプリIDです。Firebaseのプロジェクトを開き、歯車マークのプロジェクトの設定にてAndroidアプリを選択すると、「アプリ ID」が表示されます。
- FIREBASE_APP_TOKEN：ターミナルにて下記コマンドを打つと表示されるので、コピーします。

```bash
firebase login:ci
```

---

これでyamlファイルの設定は完了です！！

### AndroidアプリをApp Distributionにデプロイ！

それでは、これまでに作成したyamlファイルをGitHubにプッシュしましょう！

```bash
git add .
git commit -m "add android_cicd.yaml"
git push
```

ブラウザでGitHubを開き、Actionsタブを押すとビルドが実行中になっているはずです！

あとは気長に待ちましょう〜。

※もしうまくいかなかった場合は、GitHubのActionsタブから該当コミットを開き、エラー内容を確認しましょう！
※僕はSecretsの値が間違ってたりしてかなり時間がかかりました。。

ビルドがうまくいくと、App Distributionに設定した上司のメールアカウントにメールが届きます！

あとはメールに記載の通りの手順で、招待を承認してアプリをダウンロードすればアプリを起動できます！簡単！！

Androidは以上です！いかがだったでしょうか？

次は、もっと時間かかるかも？ でも楽しい！ なiOSに挑みます！

# 6. GitHub ActionsとfastlaneでFirebase App DistributionにiOSアプリをデプロイ

では、iOSアプリをApp Distributionにデプロイしていきたいと思います！
聞き慣れない「fastlane」という単語がありますね…？
まずはこれから説明します！

（FirebaseにFlutterにfastlane…。僕の好きなローマ字はFです！ どうでもいい1文すみません！！）

# fastlaneとは？

モバイルアプリのリリース作業を自動化しまっせ、というもの。

iOSでは証明書の生成とか管理から、アプリのビルド・リリース、そしてApp Storeの申請に必要なスクリーンショットまでもfastlaneを通してアップロード、なんてこともできてしまいます。

先ほどAndroidではFlutterコマンドでapkファイルの作成を行いましたが、実はfastlaneでもできてしまい、スクリーンショットのアップロードやplay storeへのapkのアップロードもできてしまいます。

すっごく多機能で便利です。是非使いこなせるようになりたいですね。。

詳細は[公式](https://docs.fastlane.tools/)をご覧ください！

# fastlaneを使用する準備をしよう

では早速fastlaneを使ってみましょう！

iosディレクトリに移動し、下記のどちらかのコマンドを実行しましょう！
(パーミッションがないと表示される場合はsudoをつけましょう！)

```bash
gem install fastlane

brew install fastlane
```

そして、下記コマンドを実行！
途中で「fastlaneでなにすんの？簡単に設定してあげるよ？」と聞かれるので、「マニュアルで設定します！」と答えてください。

```bash
fastlane init
```

iosディレクトリの中にfastlaneフォルダができましたので、早速実装していきましょうー！

…とその前に、チームで証明書とかを共有するのに便利なものを使う準備をしたいと思います！

その名も、「fastlane match」です！

GitHubにプライベートなリポジトリを作成し、そこに証明書関係をアップロードすることで、みんなに共有できる、というものです。もちろんGitHub Actionsにも共有できます！

今回はこちらを使用していきたいと思います。
とてもわかりやすい記事がありました！一度ご覧いただければと思います。
[fastlane match を使用して iOS の証明書管理を行う](https://qiita.com/kotarella1110/items/840af2cf80aaea1fb035)

[公式サイト](https://docs.fastlane.tools/actions/match/#match)のリンクも載せておきます。

## fastlane matchを使用する準備をしよう

では、fastlane matchを使用する準備をしていきます。

### GitHubリモートリポジトリの作成

fastlane matchで証明書を共有するGitHubリモートリポジトリを作成しましょう。
証明書関係は漏れると大変なため、プライベートリポジトリで作成してください！
今回は「flutter_cicd_certificate」というリポジトリを作成します。

### Automatically manage sigingを無効にする

Xcode プロジェクト設定「 General > Automatically manage signing 」を無効にしておきましょう！こちら有効にしたままだとうまく動かないことがありました。。

### App IDを作成する

fastlaneのproduceを使用して、ご自身のApple Developer PortalにApp IDを作成できちゃいます。
下記のコマンドを実行してみましょう！
途中でご自身のAppleアカウント、Bundle ID、アプリ名を入力することとなるため、適宜入力してください！

```bash
fastlane produce --skip_itc
# --skip_itcでApp Store Connectでのアプリ作成をスキップできます！
```

### Deviceの登録

もし上司のDeviceをまだDeveloper Portalに登録していなければ下記コマンドで登録できます！
[名前]の箇所は任意の名前、[UDID]は上司のスマホのUDIDを入れてください！

```bash
fastlane run register_device name:"[名前]" udid:"[UDID]"
```


※補足
もしプロビジョニングプロファイルを生成した後に追加でデバイスを登録したくなったら、
上記のコマンドの後に下記コマンドを実行すれば再度プロファイルを生成してくれます！楽ちん！

```bash
fastlane match adhoc --force_for_new_devices
```

### match初期設定

下記コマンドをiosディレクトリで実行しましょう！

すると、質問がいくつか出てくるので、
「git」で使用するを選択し、
先ほど作成した証明書用のGitHubリモートリポジトリのURLを記入しましょう！

```bash
fastlane match init
```

ios/fastlaneフォルダにMatchfileが作成されていることを確認しましょう！

### Matchfileの書き換え

下記の内容に書き換えましょう！

```:ios/fastlane/Matchfile
if ENV["PERSONAL_TOKEN"] != nil
  git_url("https://" + ENV["PERSONAL_TOKEN"] + "@github.com/[ユーザー名]/[作成したgitリポジトリ名]")
else
  git_url("https://github.com/[ユーザー名]/[作成したgitリポジトリ名]")
end
storage_mode("git")

type("adhoc")

app_identifier("[app_identifier]") 
```

PERSONAL_TOKENというものが出てきましたね。

これはGitHubので作成できる、パスワードの代わりに使用できるトークンのようです！
これがないとGitHub Actionsで実行した際にエラーになりました。。
こちらを使用すると複雑なSSHの設定をすることなくリポジトリにアクセスできるということのようです。

PERSONAL TOKENは、
GitHubのアカウントの「Settings > Developer settings > Personal access tokens」
から作成できます。
権限は「repo」の権限を付与しておけば大丈夫です。
表示されたTOKEN文字列をSecretsに**「PERSONAL_TOKEN」**というキーで登録しましょう！
（文字列は一度しか表示されないので、しっかりメモしておきましょう！）

ENVというのは環境変数のことで、
あとでGitHub ActionsのyamlファイルでPERSONAL_TOKENのENVを定義してあげます。

また、ローカル環境でmatchを実行した時にPERSONAL_TOKENがないですよーというエラーにならないようif文をつけています。

app_identifierの箇所はご自身のプロジェクトのBundleIDに書き換えてください。

### matchを使用してプロビジョニングファイルの作成

次に、matchを使用して証明書とプロビジョニングプロファイルを作成しましょう！

基本的にはプロジェクトの管理者が行う作業であり、管理者が作成したら他の人は「—readonly」をつけて作成せずローカルにダウンロードするだけとなります！

下記コマンドを打つと、自動でプロビジョニングプロファイルが作成されます！簡単！

途中、Appleアカウントのユーザー名を聞かれたり、
自分のパソコンのキーチェーンにアクセスするためのパスワードを聞かれたり、
match用のパスワードを設定することとなります。
このmatch用に設定したパスワードは、
GitHubのSecretsに**「IOS_CERTIFICATE_PASSWORD」**キーで登録しておきましょう！

```bash
fastlane match adhoc
```

これでmatchの設定は終了です！

ではfastlaneに…

の前にもう一つご説明します。
ipaファイルを作成するのに「fastlane gym」というものを使用します。

## fastlane gymを使用する準備をしよう

iosディレクトリで下記コマンドを使用して、Gymfileを作成しましょう！

```bash

fastlane gym init
```

ios/fastlaneフォルダにGymfileが作成されたと思うので、こちらに下記内容を記載します。

```:ios/fastlane/Gymfile
scheme("Runner")
clean(true)
export_method("ad-hoc")
codesigning_identity("Apple Distribution: ")
output_directory("./build")
```

行っていることしては、
スキーマを選択し、
ビルド前にクリーンな状態にして、
Apple Distributionの配布用証明書を使用し、
./buildディレクトリにipaファイル吐き出す、といった具合です！

それではfastlaneの設定に移っていきましょう！

## fastlaneの設定

fastlaneを使用したCI/CDの全体の流れとしては、

GitHub Actions実行！
→ fastlaneを実行！
→ matchやgymを呼び出す
→ Firebase App Distributionにデプロイ！

という感じになります。

最後のApp Distributionへのデプロイもfastlaneでできちゃうので、これもfastlaneでやってしまいましょうー！

では、下記の内容をFastFileに記載してください。

```:fastlane/Fastfile
default_platform(:ios)

platform :ios do
  before_all do |lane, options|
    if is_ci
      ENV['MATCH_KEYCHAIN_NAME'] = 'TempKeychain'
      ENV['MATCH_KEYCHAIN_PASSWORD'] = 'TempKeychainPassword'
      create_keychain(
	      name: ENV["MATCH_KEYCHAIN_NAME"],
	      password: ENV["MATCH_KEYCHAIN_PASSWORD"],
	      timeout: 1800
	    )
    end
  end

  lane :build do
    match(readonly: is_ci)

    update_code_signing_settings(
      use_automatic_signing: false,
      code_sign_identity: "iPhone Distribution",
      team_id: ENV["sigh_[Bundle ID]_adhoc_team-id"],
      profile_name: ENV["sigh_[Bundle ID]_adhoc_profile-name"]
    )

    increment_build_number(
      build_number: ENV["BUILD_NUMBER"]
    )

    gym()

    firebase_app_distribution(
        app: ENV["FIREBASE_IOS_APP_ID"],
        groups: "company",
        firebase_cli_token:  ENV["FIREBASE_APP_TOKEN"],
        release_notes: "ベータ版配信",
    )
  end
end
```

それでは一つずつ見ていきましょう！！

---

```
default_platform(:ios)
```

デフォルトのプラットフォームを定義しなきゃいけないようです。

（こちらなくても動くのかと思ったら、動きませんでした。。）

---

```
platform :ios do
```

platformはiOSですよーと定義。

（こちらはなくても一応動きはするようです）

---

```
before_all do |lane, options|
    if is_ci
      ENV['MATCH_KEYCHAIN_NAME'] = 'TempKeychain'
      ENV['MATCH_KEYCHAIN_PASSWORD'] = 'TempKeychainPassword'
      private_lane :create_temp_keychain do
		  create_keychain(
		    name: ENV["MATCH_KEYCHAIN_NAME"],
		    password: ENV["MATCH_KEYCHAIN_PASSWORD"],
		    timeout: 1800
		  )
    end
  end

```

before_allで定義したものは、fastlaneの処理が開始される前に一度だけ実行されます。

その後の|lane, options|は引数です。

下記コマンドを実行すると、laneに〇〇、optionsにkey:valueが入るようです。

```
fastlane 〇〇 key:value1 
```

if is_ciはfastlaneがCI環境かどうかをboolで返してくれるみたいです。要するに、GitHub Actionsの場合ifの中に入ります。

そして、「MATCH_KEYCHAIN_NAME」「MATCH_KEYCHAIN_PASSWORD」のENV（環境変数）に適当な文字列を割り当てているのですが、こちらを「create_temp_keychain」で使用します。

この環境変数をしていすると、matchでしようしてくれるようになるらしいです。

参考にしたサイト：[Github Actions で Firebase App Distribution にデプロイする](https://note.com/watura/n/nf49c04e54d09)

---

```
lane :build do
```

こちらでfastlaneコマンドで呼び出す名前を決定できます。

この例では「fastlane build」と呼び出すとこの中に定義されているものが動きます。

---

```
match(readonly: is_ci)
```

これで先ほど定義したMatchFileの中身が実行されます！

「readonly: is_ci」は、ciで実行する際は先ほど作成した証明書を利用するようにするために定義します。読み取り専用ということですね。

---

```
update_code_signing_settings(
  use_automatic_signing: false,
  code_sign_identity: "iPhone Distribution",
  team_id: ENV["sigh_[Bundle ID]_adhoc_team-id"],
  profile_name: ENV["sigh_[Bundle ID]_adhoc_profile-name"]
)
```

matchで取得したProvisioning Profileにupdate_code_signing_settingsで置き換えます！

automatic signingはしない、かつ配布用の設定にしています。
ENVの箇所では、team_idとadhocのprofile名を取得するために、matchを使用したときに格納される環境変数の値を取得しています。

[Bundle ID]の箇所はご自身のBundle IDに置き換えてください！

---

```
increment_build_number(
	build_number: ENV["BUILD_NUMBER"]
)
```

GitHub Actionsのyamlファイルから渡された環境変数で、
increment_build_numberを使用してBuild Numberを変更します！

---

```
gym()
```

これでGymFileを使用してipaファイルの作成ができます！

---

```
firebase_app_distribution(
    app: ENV["FIREBASE_IOS_APP_ID"],
    groups: "company",
    firebase_cli_token:  ENV["FIREBASE_APP_TOKEN"],
    release_notes: "ベータ版配信",
)
```

こちらでFirebaseAppDistributionへのデプロイができます！簡単！

この「firebase_app_distribution」というプラグインを使用するには、
予め下記コマンドを実行しておく必要があります！
これを実行すると、「Pluginfile」が作成され、そこに記載されたfirebase_app_distributionプラグインを読み込んでくれるようになります。

```
fastlane add_plugin firebase_app_distribution
```


また、Androidの時同様、
FirebaseのプロジェクトからiOSのAPP IDを取得し、
コマンドラインで「firebasde login:ci」を使用してトークンを取得します！
（FIREBASE_APP_TOKENはAndroid時に登録していれば、再登録は不要です！）

release_notesをつけておけば、配布時にその文言が表示されます！

---

ここまででfastlaneの設定は完了です！
残すはGitHub Actionsの設定のみですー！！

## GitHub Actionsの設定

.github/workflowsフォルダに「ios_cicd.yaml」ファイルを作成し、以下の内容を記載しましょう！

```yaml:.github/workflows/ios_cicd.yaml
name: ios_cicd

on:
  push:
    branches:
      - main

jobs:
  build:

    runs-on: macOS-latest
    timeout-minutes: 20

    steps:
      - uses: actions/checkout@v2

      - name:  Translate secrets of base64 into json
        env:
            GOOGLE_SERVICE: ${{ secrets.IOS_GOOGLE_SERVICE_INFO }}
        run:
            echo $GOOGLE_SERVICE | base64 --decode > ./ios/Runner/output.json

      - name:  Translate json into plist and generate GoogleService-info.plist
        run:
          plutil -convert xml1 ./ios/Runner/output.json -o ./ios/Runner/GoogleService-info.plist

      - name: Remove json file
        run:
          rm ios/Runner/output.json

      - uses: subosito/flutter-action@v1.4.0
        with:
          channel: 'stable'

      - name: Flutter setup
        run: |
          flutter pub get
          flutter test
          flutter build ios --release --no-codesign

      - name: build and app distribution
        env:
          PERSONAL_TOKEN: ${{ secrets.PERSONAL_TOKEN }}
          MATCH_PASSWORD: ${{ secrets.IOS_CERTIFICATE_PASSWORD }}
          FIREBASE_IOS_APP_ID: ${{secrets.FIREBASE_IOS_APP_ID}}
          FIREBASE_APP_TOKEN:  ${{secrets.FIREBASE_APP_TOKEN}}
          BUILD_NUMBER:  $GITHUB_RUN_NUMBER
        run: |
          cd ios
          fastlane build
```

それでは解説します！Androidの方で解説している内容もあるので、被っている箇所は割愛します！

---

```yaml
name: ios_cicd

on:
  push:
    branches:
      - main

jobs:
  build:
```

ios_cicdという名前で、mainブランチにプッシュされた時に走るようにします。
buildという名前のjobを作成します。

---

```yaml
runs-on: macOS-latest
timeout-minutes: 20

steps:
  - uses: actions/checkout@v2
```

今回はiOSのビルドを行うため、macOSを使用します。

タイムアウトは20分で設定しています。

mainブランチをチェックアウトします。

---

```yaml
- name:  Translate secrets of base64 into json
  env:
      GOOGLE_SERVICE: ${{ secrets.IOS_GOOGLE_SERVICE_INFO }}
  run:
      echo $GOOGLE_SERVICE | base64 --decode > ./ios/Runner/output.json
- name:  Translate json into plist and generate GoogleService-info.plist
  run:
    plutil -convert xml1 ./ios/Runner/output.json -o ./ios/Runner/GoogleService-info.plist
- name: Remove json file
  run:
    rm ios/Runner/output.json
```

こちらはAndroidと同様、Google-Service-Info.plistをbase64でデコードしています。
Secretsに登録する「IOS_GOOGLE_SERVICE_INFO」ですが、Androidのときより1工程作業が増えます。

まず、plistを下記コマンドでjsonに変換します。

```yaml
plutil -convert json GoogleService-info.plist -r -o output.json
```

その後、Androidのとき同様、下記コマンドでbase64でエンコードした値を取得し、
Secretsに**「IOS_GOOGLE_SERVICE_INFO」**というキーで設定します。

```yaml
cat output.json | base64
```

作成したoutput.jsonは削除するか、.gitignoreをしておいてください。

yamlファイルで行っていることはこの逆で、base64をデコードしてjsonファイルを作成し、plistファイルに変換する、ということを行っています。

---

```yaml
- uses: subosito/flutter-action@v1.4.0
  with:
    channel: 'stable'

- name: Flutter setup
  run: |
    flutter pub get
		flutter test
    flutter build ios --release --no-codesign
```

flutterコマンドを使用してbuildします。
あとでmatchでサインするためコード署名はしません。

---

```yaml
- name: build
  env:
    PERSONAL_TOKEN: ${{ secrets.PERSONAL_TOKEN }}
    MATCH_PASSWORD: ${{ secrets.IOS_CERTIFICATE_PASSWORD }}
    FIREBASE_IOS_APP_ID: ${{secrets.FIREBASE_IOS_APP_ID}}
    FIREBASE_APP_TOKEN:  ${{secrets.FIREBASE_APP_TOKEN}}
		BUILD_NUMBER:  $GITHUB_RUN_NUMBER
  run: |
    cd ios
    fastlane build
```

ラストです！
fastlaneに渡すための環境変数を定義して、
fastlane buildを行い、
ipaファイルの作成・App Distributionへのデプロイを行います！

---

以上でiOSアプリの配信の準備が整いました！

あとは、プロジェクトのルートディレクトリに戻り、下記コマンドを実行してGitHub Actionsを動かしてみましょう！

```bash
git add .
git commit -m "add ios cicd files"
git push
```

うまく実行できると、上司のメールアカウントにApp Distributionからメールが届くはずです！

手順に従いダウンロードしてもらい、アプリを検証してもらいましょう！！

# おわりに

以上でFlutterで作成したAndroidとiOSアプリをすぐに上司に検証してもらえるようになりました。

昇進昇進。

今回ご紹介した方法以外にも配布方法はあると思うので（fastlaneを使用したAndroidの配布等）、どの方法がしっくりくるか試してもらえればと思います！（何の方法がよかったか共有してもらえると嬉しいです…！）

初めてGitHub Actions・fastlaneを使用しました。
間違っている箇所がもしあれば、ご指摘いただけると嬉しいです！！

想定よりもかなり長い記事になってしまいましたが、誰かのお役に立てれば幸いです…！！