FastlaneとGitlabRunnerでCI/CDパイプラインを構築する

# 概要
Include Testにチェックを入れて、Xcodeで適当にプロジェクトを作ってください。
GitlabリポジトリにPushするとGitlabRunnerのジョブが実行され、
自動でテストが実行された後、TestFlightに自動でデプロイするパイプラインを作っていきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648522/bf3f564c-6a1c-61f5-8701-74f50c83b623.png)


# Fastlaneのセットアップ
公式Doc：
http://docs.fastlane.tools/getting-started/ios/setup/
参考：
https://www.runway.team/blog/how-to-build-the-perfect-fastlane-pipeline-for-ios

Fastlaneは証明書やプロビジョニングファイルの処理、テスト実行、ビルド、TestFlightへのアップロードなど、iOS開発をする上での面倒な部分を自動化してくれるツールです。

まずはFastlaneのインストール
```zsh
brew install fastlane
```

Fastlaneの初期化をします。
プロジェクトのルートディレクトリで以下を実行してください。
```zsh
fastlane init
```
Fastlaneがアクションを1つ選択するように求めてくるかと思います。
今回は複数のアクションを実装していきますので、4の`Manual Setup`でOKです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648522/faf2afff-0a58-95e4-9821-fcba8ea48a83.png)
このあと、laneとは？カスタマイズするには？みたいな説明が出てきてくれますので、適宜読んでEnterを押してください。

プロジェクトのルートディレクトリに`Gemfile`、 `Gemfile.lock`と
ディレクトリ`fastlane`、そしてその中に`Fastfile`、`Appfile`が作成されているのが確認できるかと思います。

Appfileに下記を追加
※プロジェクトのBundle Identifierが`com.my-app.myapp`の例
```
app_identifier(“com.my-app.myapp”)
```
### 認証の設定
fastlaneは証明書の発行やTestFlightへのアップロードなど、App Store Connectの操作もします。
そのためのAPIキーを作成しましょう。
1. [App Store Connect](https://appstoreconnect.apple.com/login)にログインする
1. **ユーザーとアクセス**に移動
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648522/a21b2a15-fff7-3c03-862d-3ea2c8289283.png)
1. 「キー」を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648522/2b34f00b-f19f-fb01-6672-9ad65e5e92e6.png)
1. 「Generate API key」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648522/f9286c05-ed4c-13f4-6eda-e0f67a11f274.png)
1. キーの名前を入力
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648522/ce4c5b1c-672d-03f9-a418-a6456e594553.png)
1. ロールを選択します。pilotを使ってTestFlightにアップロードするためには`App Manager`の権限が必要です。
1. 「Generate」をクリック
キーが正常に作成されると、必要な情報が表示されます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648522/f51c8cb8-3ef0-a9d3-595b-0a09394e5d3a.png)

作成したAPIキーの情報をFastlaneに伝えます。
```
app_store_connect_api_key(
   key_id: "D83848D23",
   issuer_id: "227b0bbf-ada8-458c-9d62-3d8022b7d07f",
   key_content: "ABC123" // base64 でエンコードされたキー,
   is_key_content_base64: true,
   in_house: false #チームが Enterprise かどうかのブール値
)
```
`key_content`には、ダウンロードした.p8ファイルの中身を以下のようにbase64エンコードして渡します。
```zsh
cat your_key_name.p8 | base64
```
ただし、このファイルはGitで管理していくことになるので、
漏洩のリスクを考えると、コードとして残したくありません。
Fastlaneには、設定されていると自動的に読み込まれる環境変数があります。
`app_store_connect_api_key`の引数として値を渡す代わりに、下記の環境変数を設定しましょう。
- key_id: `APP_STORE_CONNECT_API_KEY_KEY_ID`
- issuer_id: `APP_STORE_CONNECT_API_KEY_ISSUER_ID`
- key_content: `APP_STORE_CONNECT_API_KEY_KEY`
- is_key_content_base64: `APP_STORE_CONNECT_API_KEY_IS_KEY_CONTENT_BASE64`
- in_house: `APP_STORE_CONNECT_API_KEY_IN_HOUSE`

値を環境変数で渡すことで先ほどのコードは以下のように簡単にできます。
```
app_store_connect_api_key()
```

### MatchでCertificatesとProfilesの設定する
fastlane matchは、開発チーム間で証明書を簡単に共有するための安全な方法を提供します。
必要なすべての証明書とプロビジョニングプロファイルを暗号化されたストレージに保持します。（プライベートGitリポジトリ・Google Cloud・Amazon S3など）
今回はGitlabのプライベートリポジトリを使用していきます。

プロジェクトのルートディレクトリで以下を実行します。
```
fastlane match init
```
使用するストレージを選択し、URLを入力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648522/6ad19bd2-e2a8-13ec-c719-24c8ee33a85c.png)
次にパスワードを入力すると、matchが初期化され、ディレクトリ`fastlane`に`Matchfile`が作成されたことが確認できます。

開発用・AppStoreまたはTestFlight用のプロファイルを作成するには、以下を実行します。
```zsh
fastlane match development
fastlane match appstore
```
Apple Developer Portalにプロファイルや証明書が作成されていること、
キーチェーンアクセスに証明書が追加されていること、
指定したGitリポジトリに証明書やREADMEが追加されていることを確認してください。

次に証明書を同期するレーンを作成しましょう。
`Fastfile`を開き、以下を追加します。
```
platform :ios do
  desc "Sync certificates"
  lane :sync_certificates do
    match({readonly: true, type: "appstore"})
  end
end
```

### gymで.ipaファイルをビルドする
アプリを配信するために.ipaファイルを作成するプロセスを、gymを使うことで自動化できます。
```zsh
fastlane gym init
```
ディレクトリ`fastlane`に`Gymfile`が作成されるはずです。
以下を追加しましょう。
```
# App scheme name
scheme("ToDo")
#provide provisioning profiles to use
export_options({
   method: "app-store",
   provisioningProfiles: {
     "com.devplanet.todo" => "match AppStore com.devplanet.todo",
   }
})

# Specify the path to store .ipa file
output_directory("./fastlane/builds")

# Excludes bitcode from the build
include_bitcode(false)

# Excludes symbols from the build.
include_symbols(false)
```
環境に応じて（開発デバイスで動かす時、TestFlight配信時、リリース時など）、REST APIの参照先を変えるために、schemeは複数個（Debug、Staging、Releaseなど）作っていることが多いと思いますので、適切に指定してください。

また、TestFlightやAppStore用にビルドする時は、ビルドナンバーをインクリメントさせたいので、
Versioningの設定を変更しましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648522/13198062-3fdd-65a7-13e0-275634d2ec06.png)

`Fastfile`に以下を追加してください。
```
platform :ios do
  desc "Create ipa"
  lane :build do
    sync_certificates
    increment_build_number
    gym
  end
end
```

Automatically manage signingを無効化して、適切なプロビジョニングプロファイルをインポートしてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648522/fb7d66be-2ee9-0260-1f59-4395ce152086.png)

ターミナルで以下を実行してください
```zsh
fastlane build
```
ディレクトリ`fastlane/builds`に.ipaファイルが作成されたと思います。

### pilotでTestFlightにアップロードする

`Fastfile`に以下を追加します。
```
platform :ios do
  desc "Upload to TestFlight"
  lane :beta do
    build
    pilot
  end
end
```
ターミナルで以下を実行します。
```
fastlane beta
```
無事終了するとTestFlightでの配信が完了します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648522/03f24f1c-d38e-1097-f999-22bd169040f5.png)
また、以下のコマンドでテスターを追加することもできます。
```zsh
fastlane pilot add email@invite.com -g group-1,group-2
```

### 単体テストを自動で実行する
以下を`Fastfile`に追加してください。
```
platform :ios do
  lane :tests do
    run_tests(
      scheme: "ToDo"
    )
  end
end
```
schemeの編集画面でテストのtargetが追加されている必要がある点に注意してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648522/d422394a-3e12-bd9d-6945-5b4839424ad0.png)

以下をターミナルで実行します。
```
fastlane tests
```


# Gitlab Runnerを設定する
公式Doc：https://docs.gitlab.com/runner/install/osx.html

### まずはインストール(Apple Silicon-based systems)
```zsh
sudo curl --output /usr/local/bin/gitlab-runner "https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-darwin-arm64"

sudo chmod +x /usr/local/bin/gitlab-runner
```

### GitlabにRunnerを登録

以下をターミナルで実行
```zsh
gitlab-runner register
```
GitlabインスタンスのURLを聞かれるので入力します。
`Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com )`
→`https://your-gitlab-host-name.com`

Runnerを登録するためのトークンを取得して入力します。
`Please enter the gitlab-ci token for this runner`

トークンはここから取得できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648522/c422ec1b-b171-fea1-a955-fb181869d575.png)

Runnerの説明とタグは適宜入力し、Runner excuterを指定します。
今回はshellを選択します。

`<Home>/Users/<user>/gitlab-runner/config.toml`が作成されていることを確認してください。
この構成ファイルは、MacにインストールされているすべてのRunnerを一覧表示します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648522/015b7440-c7e7-828f-3be1-cb027c166438.png)

ターミナルで以下を実行します
```zsh
gitlab-runner run
```
画像のようにRunnerがグリーンになっていることを確認してください
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648522/9a06977a-003d-6017-208a-44124fa56c01.png)

### ジョブを構成する

プロジェクトのルートディレクトリに`.gitlab-ci.yml`を作成し、以下のように記述してください。
```yaml
stages:
  - test
  - deploy

test:
  stage: test
  script:
    - fastlane tests

deploy:
  stage: deploy
  script:
    - fastlane beta
```

# パイプラインを実行する
ここまでの変更をコミットして、プッシュしてみましょう。
`.gitlab-ci.yml`の定義通りにジョブが実行され、
Fastlaneでテスト・ビルド・デプロイが自動で完了すれば完成です！