fastlane経由でiOSアプリをFirebaseAppDistributionからテスト配信する時は、Rubyについて知っておくほうが良かった話

# はじめに
こんにちは、iOSエンジニアの [dayossi](https://twitter.com/dayossi) です。

家族が幸せになれるサービスを提供したいと思って、
[HaloHalo](https://hallo-hallo.sakura.ne.jp/home/)という家族日記アプリをリリースしています。

今回は、Firebaseが提供しているアプリのテスト配布ツール`Firebase App Distribution`を
fastlaneから呼び出して、テスト配布を自動化する中で詰まった点を共有いたします。

fastlaneがRubyで構成されているにもかかわらず
私はRubyでの開発は全くやったことがないので、
ほぼ手探り状態でfastlaneを試しました。。

先人の方々の叡智を参考に
なんとかfastlaneを実行してipaファイルをビルドできる状態まで辿り着きましたが

肝心のFirebase App Distributionへデプロイできず、
かなり詰まりました。

同じような境遇で悩まれている方の参考になりましたら幸いです。


## 前提
以下の構成で、fastlaneからFirebase App Distributionへデプロイするつもりでした。

```terminal:ファイル構成
（fastlaneを実行したいプロジェクト名）
   ├ （各種プロジェクトファイル）
   ├ Gemfile # fastlane実行・プラグイン呼び出しを管理。
   ├ Gemfile.lock
   └ fastlane
       ├ .env.default # 環境変数を管理。以下のファイルから参照される。
　　　　　　　　　　　　　├ Appfile      # app_identifier, apple_idを管理
       ├ Fastfile     # fastlane実行内容を管理
       └ Pluginfile   # fastlane実行時に使用するプラグイン実行を管理
```
```ruby:fastlane/Fastfile
default_platform(:ios)

platform :ios do
  desc "deploy STG on Firebase App Distribution"
  lane :distribution do
    # cocoapodsインストール
    cocoapods
    # .ipaファイルの作成
    gym(
      workspace: ENV["WORK_SPACE_NAME"],
      scheme: ENV["SCHEME_NAME"],
      export_method: "ad-hoc",
      export_options: {
        method: 'ad-hoc',
        provisioningProfiles: {
         ENV["BUNDLE_IDENTIFIER"] => ENV["TARGET_PROVISIONING_PROFILES"],
        },
      },
      export_xcargs: "-allowProvisioningUpdates",
    )

    # firebase app distributionに.ipaアップロード
    firebase_app_distribution(
      app: ENV["FIREBASE_APP_ID"],
      groups: ENV["TEST_TARGET_NAME"],
      release_notes: "this is fastlane test",
      # firebase_cli_path: "node_modules/.bin/firebase",
    )
  end
end
```
```ruby:Gemfile
# frozen_string_literal: true

source "https://rubygems.org"

gem "fastlane"

gem "cocoapods"

plugins_path = File.join(File.dirname(__FILE__), '.', 'Pluginfile')
eval_gemfile(plugins_path) if File.exist?(plugins_path)
```
```ruby:Pluginfile
# Autogenerated by fastlane
#
# Ensure this file is checked in to source control!

gem "fastlane-plugin-firebase_app_distribution", git: "https://github.com/fastlane/fastlane-plugin-firebase_app_distribution"
```

* Fastlaneのdistributionレーンを実行すると、以下の順序で処理が実行される想定
    * Xcodeのアーカイブで生成される ipaファイルを作成
    * firebase app distributionへipaファイルをアップロードする
    * テストアプリ配布が完了
* `fastlane-plugin-firebase_app_distribution`を、bundleコマンドでインストール済み
* 対象レーンは問題なく呼び出せ、実行可能
* `distribution`アクション内の`firebase_app_distribution`アクションは、Pluginfileに定義したgemから呼び出す想定

## ことの始まり
上記の前提をもとに、いざレーンを呼び出してみましたが。。

```terminal:ターミナル（問題箇所 修正前の実行結果）
[11:11:50]: Could not find action, lane or variable 'firebase_app_distribution'. Check out the documentation for more details: https://docs.fastlane.tools/actions

+------+------------------+-------------+
|           fastlane summary            |
+------+------------------+-------------+
| Step | Action           | Time (in s) |
+------+------------------+-------------+
| 1    | default_platform | 0           |
| 2    | cocoapods        | 1           |
| 3    | gym              | 36          |
+------+------------------+-------------+

[11:11:50]: fastlane finished with errors

[!] Could not find action, lane or variable 'firebase_app_distribution'. Check out the documentation for more details: https://docs.fastlane.tools/actions
```
`distributionレーン内で定義した、firebase_app_distributionというaction, レーン・変数は見つかりませんでした`のようなエラー表示が出て終了しました。

Pluginfileに記載したファイルから
`firebase_app_distribution`アクション・レーンなりが見つかりそうなものなんですが
うまくいきませんでした。

プラグインを呼び出せない原因を探ってみたところ、
問題はGemfileでのプラグイン呼び出しに原因がありました。

## 今回の問題分析
### やりたかったこと
fastlaneを実行したいプロジェクト内の`fastlane/Pluginfile`で指定したプラグインのファイルを実行したい。

### 分析
そもそもRuby自体をほとんど触ったことがなく、基本的なところの理解ができていなかったので
まずは「fastlane実行時にプラグインを呼び出すには？」を整理しました。

#### fastlane実行時にプラグインを呼び出すには？

大まかな流れは、以下のように解釈しました。
```
1. bundle exec fastlane （レーン名）実行
2. Gemfile.lockを見る
3. Gemfile内のgemからfastlaneを実行
4. Gemfile内の他のgemを実行
5. Fastfileを見る
6. 各レーン内で、定義されたアクションを実行する
※ fastlaneの基本アクション以外のアクションを使用する場合は、別途アクションファイルを読み込んでおく必要がある
```
今回は、6.で`'firebase_app_distribution'というアクションはありません`エラーが出ましたので
まずは`firebase_app_distribution`アクションの出どころを整理しました。

[fastlaneで定義されている基本アクション](https://docs.fastlane.tools/actions/)の中には、`firebase_app_distribution`というアクションはありませんでした。

`firebase_app_distribution`アクションは、外部から別途ファイルを読み込む必要がありました。
（アクション提供元：https://github.com/fastlane/fastlane-plugin-firebase_app_distribution）

Pluginfileには記載していたので、
4. Gemfile内の他のgemを実行
で何か不具合があると推測しました。

外部プラグインを呼び出す箇所は、以下の通りです。

```ruby:Gemfile
source "https://rubygems.org"

gem "fastlane"

gem "cocoapods"

# 以下、問題箇所
# プラグインを実行するファイルパスを取得する
plugins_path = File.join(File.dirname(__FILE__), '.', 'Pluginfile')
# 以上、問題箇所

# 上記で指定したファイルパスから、ファイルを読み取ってインスタンス化する
eval_gemfile(plugins_path) if File.exist?(plugins_path)
```

### 記述内容の整理
Fileは、RubyのFileクラスを指します。
File.joinは、各引数で指定した文字列を結合するFileクラスの関数です。
File.dirname(__FILE__)は、宣言されたファイルのパスを返すFileクラスの関数です。
（この場合は、Gemfileのパスを取得します）

本当なら `（プロジェクト内のGemfileのパス）/fastlane/Pluginfile` のようなパスを`plugins_path`に渡す必要がありますが、

`File.join`の第二引数が '.'となっており、
`（プロジェクト内のGemfileのパス）/(プロジェクト内のGemfileのパス)/Pluginfile` というパスが渡されていました。

そのため、ファイル読み取りに失敗し
`'firebase_app_distribution'というアクションはありません`と警告されていたようでした。

### 対応
第二引数に 'fastlane'を指定し、`（プロジェクトのルートパス）/fastlane/Pluginfile` となるように修正しました。
```ruby:Gemfile（修正後）
source "https://rubygems.org"

gem "fastlane"

gem "cocoapods"

# 以下、問題箇所
# （プロジェクトのルートパス）/fastlane/Pluginfile を取得できるように修正
plugins_path = File.join(File.dirname(__FILE__), 'fastfile', 'Pluginfile')
# plugins_path = File.join('.', 'fastfile', 'Pluginfile') // この記述でも実行できました
# 以上、問題箇所

# 上記で指定したファイルパスから、プラグインを実行する
eval_gemfile(plugins_path) if File.exist?(plugins_path)
```

### 修正後の結果
```terminal:ターミナル（問題箇所 修正後の実行結果）
[10:59:13]: Using deprecated option: '--firebase_cli_path' (This plugin no longer uses the Firebase CLI)
[10:59:13]: ---------------------------------------
[10:59:13]: --- Step: firebase_app_distribution ---
[10:59:13]: ---------------------------------------
[10:59:13]: No authentication method specified. Using cached Firebase CLI credentials.
[10:59:13]: 🔐 Authenticated successfully.
[10:59:13]: ⌛ Uploading the IPA.
[10:59:17]: ✅ Uploaded IPA successfully; updated provisioning profile of existing release 1.0 (5).
[10:59:18]: ✅ Posted release notes.
[10:59:19]: ✅ Added testers/groups.
[10:59:19]: 🎉 App Distribution upload finished successfully.
```
無事にfirebase app distributionプラグインを実行でき、テスト配布できました🥳

## まとめ
そもそも、fastlaneがRubyで構成されていることを理解できていなかったため
問題の切り出しが難しいと感じていたことがわかりました。

（[fastlaneのGithub](https://github.com/fastlane/fastlane
)を見ると、しっかりRubyで構成されていることがわかるのに。。）
![スクリーンショット 2022-05-13 17.37.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/730098/403b58a5-85b6-1264-ec10-cba1aa654701.png)


ホントに初歩的なつまづきでしたが、今回の件でfastlaneの基本的なところを学べました。

## 雑記
fastlaneのfirebase_cli_pathオプションは、今後使えなくなる可能性があることを留意しておきたいと思います。

# 参考記事
* [fastlane を使用して iOS アプリをテスターに配布する](https://firebase.google.com/docs/app-distribution/ios/distribute-fastlane?hl=ja)
* [[iOS] fastlaneを導入する手順について](https://dev.classmethod.jp/articles/prepare-for-ios-app-distribution-part-2/)
* [【iOS】最新fastlane導入[備忘録]](https://yutaabe200.hatenablog.com/entry/2020/03/06/%E3%80%90iOS%E3%80%91%E6%9C%80%E6%96%B0fastlane%E5%B0%8E%E5%85%A5%5B%E5%82%99%E5%BF%98%E9%8C%B2%5D)
* [【fastlane】環境変数の設定](https://www.hfoasi8fje3.work/entry/fastlane/setting-environment-variables)
* [fastlane公式ドキュメント（環境変数を別ファイルから参照する方法を参考にしました）](https://docs.fastlane.tools/best-practices/keys/)
* [fastlane公式ドキュメント（基本アクションについて）](https://docs.fastlane.tools/actions/)
* [【初心者向け】bundler、Gemfile、Gemfile.lockの関係性について図でまとめてみた](https://qiita.com/nishina555/items/1b343d368c5ecec6aecf#:~:text=%E3%80%8EGemfile%E3%80%8F%E3%81%A8%E3%81%AFRails%E3%82%A2%E3%83%97%E3%83%AA,%E3%81%99%E3%82%8BGemfile%E3%81%AE%E5%BD%B9%E5%89%B2%E3%81%A7%E3%81%99%E3%80%82)
* [bundle execって必要なの?](https://qiita.com/d0ne1s/items/fa2dafcee02e963fe997)
* [詳解Fastfile](https://fortee.jp/iosdc-japan-2018/proposal/353f43d5-4de4-4d80-bb8f-3b7bdaadb544)
* [fastfileのGithub](https://github.com/fastlane/fastlane)
* [Ruby Fileクラス](https://docs.ruby-lang.org/ja/latest/class/File.html)