知識0からのFastlane デプロイ

プロジェクト内でFastlaneを使ってデプロイまで行うことになりました。
Fastlane初心者が一から調べてみた過程を記録します!
これから学ぼうと思う方にとって有益な情報となれば嬉しいです!:)

# やりたいこと
* TestFlightへのアップロード

# AppStoreConnectAPI
以下を参考にして、APIキーファイル(.p8)、キーID、発行者IDを保管して下さい。
ちなみに権限がないとキータブ見られないです。その場合は、APIの作成を頼むか、2FAを使用した方法になるのかと思います。

https://docs.fastlane.tools/getting-started/ios/authentication/

https://qiita.com/kurikazu/items/135a791ba09121d7a534

# AppStoreConnectAPIはなぜ必要か？
> The App Store Connect API (which Apple announced at WWDC18 and is continuously working on) is an official public API used to manage app metadata, pricing and availability, provisioning, and more. This API follows the JSON API spec and introduces a new authorization option using API Keys to generate a JSON Web Token (JWT).
> 
> App Store Connect API (Apple が WWDC18 で発表し、継続的に取り組んでいる) は、アプリのメタデータ、価格と可用性、プロビジョニングなどを管理するために使用される公式のパブリック API です。 この API は JSON API 仕様に従っており、API キーを使用して JSON Web トークン (JWT) を生成する新しい認証オプションを導入しています。

https://docs.fastlane.tools/app-store-connect-api/

https://developer.apple.com/app-store-connect/api/

# テストフライトへのアップロード
以下を参考にしてビルドとテストフライトへのアップロードを実装して下さい。

https://docs.fastlane.tools/getting-started/ios/beta-deployment/

https://zenn.dev/imajoriri/books/dbc5fc755cf079/viewer/46c9ec#testflight%E3%81%B8%E3%82%A2%E3%83%83%E3%83%97%E3%83%AD%E3%83%BC%E3%83%89%E3%81%99%E3%82%8B

https://docs.fastlane.tools/actions/gym/

ビルドについては、今回は以下のように書きました。

```ruby
platform :ios do
  desc: "TestFlightにアップロードする"
  lane :beta do
    build_app(
      project: "xxx.xcodeproj",
      scheme: "xxx",
      clean: true,
      silent: true,
      export_team_id: "xxx",
      export_method: "app-store",
      export_options: {
        provisioningProfiles: {
        "jp.co.bundle.id" => "provisioningファイル名"
        }
      },
      output_directory: "build/",
      buildlog_path: "build/",
    )
  end
end
```

`clean`や`silent`については以下に記載があります。

https://docs.fastlane.tools/actions/gym/

テストフライトについては、なんのパラメーターを入れようか‥と迷ったので、一通り見ることにしました。
パラメーターは以下に載っています。

https://docs.fastlane.tools/actions/testflight/

* `api_key`🙆‍♀️
* `username`: AppleIDのユーザー名
<details><summary>username用途</summary>
 以下はChatGPTで調べた内容です。
 
`fastlane`における`upload_to_testflight`アクションは、ビルドされたiOSアプリをTestFlightにアップロードするために使用されます。このプロセスを自動化することで、開発チームはアプリのテスト版を迅速に配布し、ベータテスターからのフィードバックを収集できます。`upload_to_testflight`アクションを使用する際、さまざまなパラメータを設定することができ、その中の`username`パラメータは特に重要です。

### `username`パラメータについて

`username`パラメータは、TestFlightにアプリをアップロードするために使用するApple IDのメールアドレスを指定するために使用します。このApple IDは、App Store Connectアカウントに関連付けられている必要があります。`upload_to_testflight`アクションが実行されると、指定された`username`（Apple ID）を使用してApp Store Connectにログインし、そこからアプリをTestFlightにアップロードします。

### 使用例

`Fastfile`内での`upload_to_testflight`の基本的な使用方法は以下のようになります：

```ruby
upload_to_testflight(
  username: "your_apple_id_email@example.com", # Apple IDのメールアドレス
  # 他のパラメータ...
)
```

この`username`パラメータは、`fastlane`がApp Store Connectへの認証を行う際に必要とされ、アプリのアップロードに先立って適切なアカウントとして認識されることを保証します。特に、複数のApple IDを管理している開発者やチームにとって、正しいApple IDを指定することは、アプリが予定通りのアカウントにアップロードされることを確実にする上で重要です。

また、セキュリティを向上させるため、`fastlane`は環境変数を通じてこのような機密情報を管理することを推奨しています。例えば、`FASTLANE_USER`環境変数にApple IDを設定し、`Fastfile`内で直接メールアドレスを記述することなく、その環境変数を参照することができます。
</details>

* `app_identifier`: テスターをアップロードまたは管理するアプリのバンドル識別子🙆‍♀️
<details><summary>app_identifierは必要か？</summary>
以下はChatGPTで調べた内容です。

`upload_to_testflight` アクションで `app_identifier` を指定する必要があるかどうかは、実行環境や設定に依存します。`app_identifier` は、特定のアプリケーションを一意に識別するために使用される文字列であり、通常はリバースドメイン名形式（例: `com.example.app`）で表されます。ビルドプロセス中に `app_identifier` を指定している場合でも、`upload_to_testflight` 実行時に明示的に指定する必要があるかどうかは、以下の点を考慮する必要があります：

### 既にビルド時に指定している場合

- **ビルド設定が `upload_to_testflight` に適切に引き継がれる場合**：`fastlane` は、ビルドプロセスで使用された `app_identifier` を `upload_to_testflight` アクション実行時に自動的に使用することがあります。これは、`fastlane` の `gym` や `build_app` アクションで生成されたビルド情報が内部的に管理され、同じ `Fastfile` 実行セッション内で後続のアクションに引き継がれるためです。

- **明示的な指定が必要なシナリオ**：しかし、ビルドプロセスとアプリアップロードプロセスが分離されている場合（例えば、ビルドを行うCIジョブとアップロードを行うCIジョブが別々に設定されている場合）、`app_identifier` を `upload_to_testflight` アクションで明示的に指定する必要があります。これにより、アップロードする正しいアプリケーションを `fastlane` が確実に識別できるようになります。

### APIキーを使用している場合

- **APIキーを使用する場合**：`api_key` を使用して `upload_to_testflight` を認証する際に、`app_identifier` の指定は必須ではない場合もあります。APIキーに紐付けられた権限とApp Store Connectの設定によっては、アップロードするアプリケーションが自動的に識別されることがあります。ただし、複数のアプリを管理している場合や、特定の環境でアップロードの対象を明確にする必要がある場合は、`app_identifier` の指定が役立ちます。

### 結論

`app_identifier` の指定は、すべてのシナリオで必須ではありませんが、特に以下の場合には明示的な指定が推奨されます：

- ビルドとアップロードが異なるプロセスまたはセッションで実行される場合
- 複数のアプリを管理している場合、または特定の環境でアプリを明確に識別する必要がある場合

最終的に、`upload_to_testflight` アクションでの `app_identifier` の扱いは、プロジェクトの具体的なニーズと設定に基づいて適宜調整することが最善です。
</details>

* `app_platform`: 使用するプラットフォーム

<details><summary>app_platform用途</summary>
以下はChatGPTで調べた内容です。

`app_platform`は、`fastlane`の様々なアクションで使用されるパラメータの一つで、特にアプリのアップロードやテスト配布を行う際に指定されることがあります。このパラメータは、操作対象となるアプリケーションのプラットフォームを指定するために使用されます。`fastlane`はiOSおよびAndroidアプリの自動化に対応しているため、特定のアクションがどのプラットフォームのアプリに対して実行されるべきかを明確にする必要がある場合があります。

### 使用例

例えば、AppleのApp Store ConnectやGoogleのPlay Consoleへのアプリのアップロード、またはTestFlightへのテスト版アプリの配布など、プラットフォーム固有の操作を`fastlane`で自動化する際に、`app_platform`パラメータを用いて対象プラットフォームを指定します。

`app_platform`で指定できる値には以下のようなものがあります：

- `ios`: iOSアプリケーションを指定します。
- `android`: Androidアプリケーションを指定します。
- `osx`: macOSアプリケーションを指定します（macOSアプリのビルドや配布に関する場合）。
- `tvos`: tvOSアプリケーションを指定します。

このパラメータは、アクションによっては必須ではない場合もありますが、複数のプラットフォームにまたがるプロジェクトを扱っている場合や、特定のプラットフォーム専用の処理を明確に区分したい場合に便利です。

### 実際の使用

`app_platform`を使用する実際の例として、TestFlightへのiOSアプリのアップロードを考えてみましょう。この場合、`upload_to_testflight`アクションで`app_platform`を`ios`として指定することで、アップロードするアプリがiOSアプリであることを明確にします。

```ruby
upload_to_testflight(
  app_identifier: "com.example.app",
  app_platform: "ios" # iOSアプリとしてアップロード
)
```

このように`app_platform`を使用することで、`fastlane`の自動化スクリプトがより明確に、かつ意図したプラットフォームに対して正確に動作するようになります。
</details>

* `apple_id`: App Store Connectの「アプリ情報」セクションのApple IDプロパティ

<details><summary>apple_idは必要か？</summary>
以下はChatGPTで調べた内容です。

App Store Connectにおける「アプリ情報」セクションで見つかるアプリ固有のApple IDは、そのアプリケーションを一意に識別する数字で構成されたIDです。このIDを`fastlane`や他のApp Store Connect APIを利用する際に指定する理由は、特に複数のアプリを管理している場合や、特定のアプリに対して操作を行いたいときに、正確にそのアプリを指定するためです。

### アプリ固有のApple IDを指定する理由

- **明確な識別**：アプリ固有のApple IDを使用することで、同一アカウント下の複数のアプリケーションの中から、操作したい特定のアプリを明確に指定できます。これは、アプリのメタデータを更新したり、バージョン情報を取得したりする際に特に重要です。
- **自動化プロセスの精度**：`fastlane`などの自動化ツールを使用する際に、アプリ固有のApple IDを指定することで、自動化されたプロセスの精度と信頼性を高めることができます。特に、CI/CDパイプライン内でApp Store Connectに関連する操作を自動化する場合に有効です。

### 必須ではないケース

アプリ固有のApple IDの指定が必須ではない場合もあります。これは、使用しているツールや実行している操作の種類によって異なります。

- **`fastlane`の一部のアクションでは不要**：例えば、`fastlane`の`deliver`や`pilot`などのアクションでは、アプリケーションのバンドルID（例: `com.example.app`）のみで十分な場合があります。これは、バンドルIDもまたアプリを一意に識別するものであり、特にApp Store Connect内で一意であるためです。
- **コンテキストによる**：アップロードやテスト配布などの操作では、アプリケーションのバンドルIDや他の識別情報がすでにコンテキスト内で明確である場合、アプリ固有のApple IDを追加で指定する必要はありません。

結局のところ、アプリ固有のApple IDの指定は、特定のアプリに対する操作を正確に行うための追加の方法として利用できますが、全てのケースで必須というわけではありません。実際に必要となるかどうかは、行いたい操作の種類や使用するツールの仕様に依存します。
</details>

* `ipa`: アップロードする ipa ファイルへのパス
* `pkg`: pkg ファイルへのパス
* `beta_app_description`: 新しいビルドをアップロードするときに「ベータ版アプリの説明」を入力してください
* `changelog`: 新しいビルドをアップロードするときに「何をテストするか」テキストを入力します🙆‍♀️
<details><summary>beta_app_description, changelog違い</summary>
以下はChatGPTで調べた内容です。

TestFlightを使用して外部テスターにアプリを配布する際、`changelog`は必須ですが、`beta_app_description`は必須ではありませんが、非常に推奨されます。

### changelog（変更ログ）

- **必須性**: 外部テスターにビルドを配布する際、`changelog`は必須です。これは、新しいビルドやアップデートを配布する際に、そのバージョンに含まれる主な変更点や改善点、新機能、バグ修正などをテスターに知らせるために使用されます。TestFlightでは、新しいビルドをテスターにリリースするたびに、そのビルドの`changelog`を入力することが求められます。
- **目的**: テスターが最新のビルドに何が含まれているか、どのような変更があったかを把握できるようにするため。特に重要なバグ修正や新機能のテストを依頼する際に役立ちます。

### beta_app_description（ベータアプリ説明）

- **必須性**: `beta_app_description`は、アプリ全体のテスト目的やフィードバックの提出方法などを説明するために使用されますが、外部テスターへの配布時には必須ではありません。しかし、テスターにアプリの目的やテストの焦点を明確に伝え、効果的なフィードバックを促すためには非常に役立つため、入力することが強く推奨されます。
- **目的**: テスターにアプリの概要、テストの目的、特定の注意点やフィードバックの求め方など、アプリテストに関する重要な情報を提供するため。

結論として、外部テスターに対して新しいビルドを配布する際には、そのビルドに何が含まれているかを伝える`changelog`の提供が必須です。一方で、`beta_app_description`は必須ではありませんが、テスターに対してアプリのテストに関する有用なコンテキストや指示を提供するため、非常に推奨される情報です。これにより、テスターからの質の高いフィードバックを得やすくなり、テストプロセスがより効率的になります。
</details>

* `distribute_external`: ビルドは外部テスターに​​配布する必要がありますか? true に設定した場合、groupsオプションの使用が必要です🙆‍♀️
* `app_version`: 配布するアプリケーション ビルドのバージョン番号。バージョン番号が指定されていない場合は、TestFlight にアップロードされた最新のビルドが配布されます。指定した場合、バージョン番号の最新のビルドが配布されます。
* `build_number`: 配布するアプリケーション ビルドのビルド番号。ビルド番号が指定されていない場合は、最新のビルドが配布されます。

バージョンについては、後で記載します！

* `groups`: グループ名/グループ ID によってテスターを 1 つ以上のグループに関連付けます。🙆‍♀️
* `team_id`: 複数のチームに所属している場合の App Store Connect チームの ID
* `dev_portal_team_id`: 複数のチームに所属している場合の、開発者ポータル内のチームの短縮ID。iTC チームIDとは異なります。🙆‍♀️

<details><summary>team_id, dev_portal_team_idの違い</summary>

正直違いがわからない‥
けど、dev_protal_team_idだけで問題ない雰囲気‥

> @KleMiX Heyyyy! This is totally related to recent developer changes. The team ids that were used for developer portal are no more and the ones that were originally on App Store Connect (iTunesConnect) are now also used in the developer portal. I would try either using "12345" for both or use -team_name "SomeCompany"
I personally use the team name 😊

https://github.com/fastlane/fastlane/issues/14395

</details>

とりあえず指定するプロパティーの選別はできた。

## changelog
いつも外部テスターに公開をしているので、`changelog`が必須になります。
changelogには、`changelog_from_git_commits`を使用してみます。

https://docs.fastlane.tools/actions/changelog_from_git_commits/

<details><summary>changelog_from_git_commits使用用途</summary>
以下はChatGPTで調べた内容です。

`fastlane`の`changelog_from_git_commits`は、Gitのコミット履歴からチェンジログを生成するための`fastlane`アクションです。`fastlane`はiOSおよびAndroidアプリのビルドとリリースプロセスを自動化するためのツールです。`changelog_from_git_commits`アクションを使用すると、指定された範囲のコミットから自動的にチェンジログを作成できます。これは、アプリの新しいバージョンをリリースする際に、変更点や修正点を簡単に文書化するのに役立ちます。

このアクションは、様々なオプションを設定することで、コミットメッセージのフィルタリングや、チェンジログのフォーマットをカスタマイズすることができます。例えば、特定のタグ間のコミットのみを対象にしたり、特定のキーワードを含むコミットのみをチェンジログに含めることができます。

基本的な使用方法は以下のようになります：

```ruby
changelog = changelog_from_git_commits
```

これにより、最後のタグから現在のHEADまでのコミットからチェンジログが生成されます。より詳細な設定や、特定の範囲のコミットを対象にする方法については、`fastlane`の公式ドキュメントや`changelog_from_git_commits`アクションのリファレンスを参照するとよいでしょう。
</details>

`merge_commit_filtering`というマージを含むか否かの設定があるので、`exclude`に設定しました。

<details><summary>merge_commit_filteringについて</summary>
以下はChatGPTで調べた内容です。

`merge_commit_filtering`オプションは、`fastlane`の`changelog_from_git_commits`アクションで使用され、Gitコミット履歴からチェンジログを生成する際に、マージコミットをどのように扱うかを指定するためのオプションです。マージコミットは、複数のブランチを統合する際に作成される特別なコミットで、通常は具体的な変更内容よりも、ブランチの統合自体を記録する目的で使用されます。

`merge_commit_filtering`オプションには、主に以下のような設定値があります：

- `include`：マージコミットをチェンジログに含める。
- `exclude`：マージコミットをチェンジログから除外する。
- `only_include_merges`：マージコミットのみをチェンジログに含め、他のタイプのコミットは除外する。

デフォルトの設定では、多くの場合マージコミットはチェンジログから除外されますが、このオプションを使用することで、チェンジログに含めるコミットの種類を細かく制御できます。例えば、リリースプロセスで特定のブランチの統合を重要なマイルストーンとして扱いたい場合、マージコミットを明示的にチェンジログに含めることが望ましいかもしれません。

このオプションの設定方法は、`changelog_from_git_commits`アクションを呼び出す際に、次のように指定します：

```ruby
changelog = changelog_from_git_commits(merge_commit_filtering: 'exclude')
```

この例では、マージコミットをチェンジログから除外する設定になっています。チェンジログの内容とその表示方法をプロジェクトのニーズに合わせて調整するために、`merge_commit_filtering`オプションを適切に設定することが重要です。
</details>

## ビルド番号
`increment_build_number`を使用してみます。

https://docs.fastlane.tools/actions/increment_build_number/

ビルド番号が被る可能性があるので、以下のように、日付と時刻からビルド番号を生成するようにします。
```ruby
## アプリのビルド番号を更新
number = sh("date +%Y%m%d%H%M").chomp
increment_build_number(
    build_number: number
)
```

## clean_build_artifacts
生成したファイルを削除するために、`clean_build_artifacts`を実行します。

https://docs.fastlane.tools/actions/clean_build_artifacts/

## crashlytics
FirebaseのCrashlyticsを使用している場合は、dSYMファイルをアップロードすることが必要になります。以下を参考にして、FirebaseCrashkyticsにdSYMファイルをアップロードしました。

https://docs.fastlane.tools/actions/upload_symbols_to_crashlytics/#parameters


## 完成コード
今回はこんな感じに書きました。
環境変数などは追々やっていこうと思います。

```ruby
platform :ios do
  desc: "TestFlightにアップロードする"
  lane :beta do
    begin
      # アプリのビルド番号を更新
      number = sh("date +%Y%m%d%H%M").chomp
      increment_build_number(
        build_number: number
      )
      build_app(
        project: "XXX.xcodeproj",
        scheme: "XXX",
        clean: true,
        silent: true,
        export_team_id: "XXX",
        export_method: "app-store",
        export_options: {
          provisioningProfiles: {
            "jp.co.bundle.id" => "プロビジョニングファイル名"
          }
        },
        output_directory: "build/",
        buildlog_path: "build/",
      )
      changelog = changelog_from_git_commits({
        merge_commit_filtering: "exclude_merges"
      })
      api_key = app_store_connect_api_key(
        key_id: "XXX",
        issuer_id: "XXX",
        key_filepath: "./fastlane/AuthKey_XXX.p8"
      )
      upload_to_testflight(
        api_key: api_key,
        app_identifier: "jp.co.XXX",
        distribute_external: true,
        groups: ["test"],
        dev_portal_team_id: "XXX",
        changelog: "#{changelog}"
      )
      upload_symbols_to_crashlytics(gsp_path: XXX)
      clean_build_artifacts
    rescue => error
      puts error
      raise
    end
  end
end
```

# 実際に実行すると
## API権限エラー
以下のエラが出ました。

```
This request is forbidden for security reasons - The API key in use does not allow this request
```

多分APIのアクセスの役割を上げないといけないかも
TestFlightへのアップロードは成功しているけど、外部テスターへの公開がうまくいっていませんでした！

https://developer.apple.com/documentation/appstoreconnectapi/creating_api_keys_for_app_store_connect_api

以下から`AppManager`にしないとダメみたいです！

https://github.com/fastlane/fastlane/issues/21797

それ以外の、ビルド、TestFlight、dSYMのアップロードはうまくいっていました！

ちなみにChangeLogはそのまま残っていて、AppStoreConnect上で外部テスターに送ろうとしたらコミットのログの記載がありました！

解決方法として、責任者に別のAppStoreConnectAPI作成してもらいましたー！

## Crashlyticsエラー
SPMを使用してFirebaseCrashlyticsを導入していると、以下のエラーが出ると思います。
```
Failed to find Fabric's upload_symbols binary at /Applications/Fabric.app/**/upload-symbols or ./Pods/**/upload-symbols. Please specify the location of the binary explicitly by using the binary_path option
```

この場合に、以下の対応を行いました。

https://sarunw.com/posts/upload-dsym-to-firebase-with-fastlane/

# まとめ
Fastlaneを使ってTestFlightへのアップロードをやっていきました！
コマンド一つで外部テスターまでリリースかつ、チェンジログをコミットから判定して書いてくれるの便利すぎました‥🙇

環境変数を使うやり方はまた時間がある時に進めていきたいと思います！