Expo アプリを EAS Build でローカルビルドしてみた

# 概要

Expo アプリは基本クラウド上でビルドしますが、EAS Build (Expo 向けの次世代ビルドサービス) にローカルビルドの機能があったので試してみました。試したのはiOSのみですが、Androidにも対応しています。

# EAS Build

EAS Buildは、Expo Application Servicesのビルドを行うための次世代のビルドサービスです。

- EAS Build (クラウド上のビルドサービス)
- EAS Submit (クラウドからアプリストアにサブミットするためのサービス)
- EAS Update (OTAでアップデートを提供するためのサービス(まだ使えない))

2021/5時点では、EAS Build、EAS Submit は、EASのPriorityプランに入ると機能プレビューとして使えるようになるみたいですが、EAS Buildのローカルビルドについては、Freeプランでも試すことができます。

なお、2021/5時点ではEAS BuildはFeature Previewなので、まだお試し段階だと思います。

# EAS Build でローカルビルドしてみる

EAS Buildは、クラウド上のビルドサービスですが、ローカルでビルドするための`--local`オプションがあります。

https://docs.expo.io/build-reference/local-builds/

以降、試したときの手順です。

## 事前準備

- Expo アカウントが必要
- Apple Developer Programアカウントが必要
- iOSアプリのローカルビルドにはMacが必要なので、Macの利用が前提

また、ローカルビルドのため、React Nativeアプリがビルドできるようにセットアップしておく必要があるかもしれません、不要なのもあるかもしれませんが下記をセットアップした状態から始めました。

- Homebrew
- Node.js
- Yarn
- Xcode
- CocoaPods
- fastlane
- expo-cli

なお、以下の手順ではiOSアプリに関連する、証明書、プロビジョニングプロファイル、デバイス登録などは、事前に用意、実施しています。

## eas-cliをインストール

EAS Buildでローカルビルドするのに必要なnpmパッケージを、グローバルインストールします。

```
% npm install -g eas-cli
% npm install -g eas-cli-local-build-plugin
```

## Expoプロジェクトを用意する

適当なExpoプロジェクトを用意し、移動します。

```
% expo init my-app
% cd my-app
```

## Expoアカウントでログインする

取得済みのExpoアカウントでログインします。

```
% eas login
Log in to EAS
✔ Email or username … Expoアカウント
✔ Password … Expoアカウントのパスワード
Logged in
```

## EAS Buildの基本設定

https://docs.expo.io/build-reference/build-configuration/

を参考に、コマンドを実行して対話的に設定します。
設定後、`eas.json`が作成されます。

```
% eas build:configure
💡 The following process will configure your iOS and/or Android project to be compatible with EAS Build. These changes only apply to your local project files and you can safely revert them at any time.
```

今回は、iOSのみ試すので、iOSを選択します。

```
? Which platforms would you like to configure for EAS Build? › - Use arrow-keys. Return to submit.
    All
❯   iOS
    Android

✔ Generated eas.json
```

app.jsonにbundleIdentifierがない場合、bundleIdentifierの入力が求められるので入力します。

```
? What would you like your iOS bundle identifier to be? › iOSアプリのbundleIdentifier
```

変更をGitにコミットするか確認されるので、差分を確認してから、コミットします。
**eas build コマンドでは、未コミットの変更があると実行できないため、コミットが必要になります。**

```
? Can we commit these changes to git for you? › - Use arrow-keys. Return to submit.
    Yes
❯   Show the diff and ask me again
    Skip committing changes, I will do it later on my own

? Can we commit these changes to git for you? › - Use arrow-keys. Return to submit.
❯   Yes
    Skip committing changes, I will do it later on my own

? Commit message: › Configure EAS Build for iOS
```

## ビルドの細かい設定

ビルドする際の細かな設定をします。

### app.json

app.jsonにビルド番号を追加します。


```diff
     ],
     "ios": {
       "supportsTablet": true,
-      "bundleIdentifier": "iOSアプリのbundleIdentifier"
+      "bundleIdentifier": "iOSアプリのbundleIdentifier",
+      "buildNumber": "1"
     },
     "android": {
       "adaptiveIcon": {

```

### eas.json

eas.jsonにビルドで指定する、プロファイルを追加します。

```diff
     "ios": {
       "release": {
         "workflow": "managed"
+      },
+      "preview": {
+        "workflow": "managed",
+        "distribution": "internal",
+        "credentialsSource": "local"
       }
     }
   }
```

上記の例では、previewというプロファイルを追加し、

- distributionを、AdHocビルド用のinternalと指定
- credentialsSourceを、証明書やプロビジョニングプロファイルの指定をローカルでするためにlocalと指定

しています。もし、ストアで公開するためのビルド(store)で、証明書やプロビジョニングプロファイルをリモートのEAS側で管理する場合(remote)は、追加はしなくてもよいです。

設定値などは、次などを参考に。

https://docs.expo.io/build/internal-distribution/

https://docs.expo.io/app-signing/local-credentials/

### 証明書やプロビジョニングプロファイルの指定

eas.jsonでcredentialsSourceをlocalとした場合、証明書やプロビジョニングプロファイルの指定をします。

証明書やプロビジョニングプロファイルを配置します。

- ios-certs/cert.p12
- ios-certs/プロファイル名.mobileprovision

置き場所は、次の設定ファイルで指定するため、ある程度任意ですが、`ios`ディレクトリを作ってしまうと、ローカルビルドが実行できなかったのでiosディレクトリ以外にした方が良さそうです。Managed Workflowとして判定されなくなる？

証明書やプロビジョニングプロファイルの情報は、credentials.json で指定します。

```json
{
  "ios": {
    "provisioningProfilePath": "ios-certs/プロファイル名.mobileprovision",
    "distributionCertificate": {
      "path": "ios-certs/cert.p12",
      "password": "p12ファイルのパスワード"
    }
  }
}
```

### .gitignore

.gitignoreに、Gitコミットの対象外ファイルを追加します。

```
 # macOS
 .DS_Store
+
+*.ipa
+ios-certs/*
+credentials.json
```

- *.ipa : ビルド後のファイル
- ios-certs/ : 証明書やプロビジョニングプロファイルを置いたディレクトリ
- credentials.json : p12ファイルのパスワードを書いているので念のため

## Git コミット

eas buildをする時、Git コミットが必要になるので、あらかじめ差分を確認してコミットします。

```
% git status
% git add .
% git diff --staged
% git commit -m "ローカルビルドの設定追加"
```

## ビルド

設定が終わったら、次のコマンドでビルドします。

```
eas build --profile preview --platform ios --local
```

- profile: eas.jsonで定義したプロファイルを指定(releaseや、preview)
- platform: iOSの場合ios
- local: ローカルビルドを実行する場合

ローカルビルド処理は、実行ログから次のような流れのようです。

- EAS サーバーに、app.jsonで指定しているslugのプロジェクトを登録する
- iOSアプリ関連のクレデンシャルの解決(ローカルでない場合、expo build同様対話形式で自動生成される)
- アーカイブプロジェクトのダウンロード
- アーカイブプロジェクトの各種設定変更
- 依存するnpm パッケージのダウンロード
- 依存するpodsダウンロード
- ローカルビルド実行(fastlane, xcodebuild, xcrun)

ビルドが成功すると、カレントディレクトリにipaファイルが生成されます。

```
Build successful
You can find the build artifacts in <カレントディレクトリ>/build-6-4-2021-13:58.ipa
```

# 補足

従来のexpo buildでは、デフォルトだとexpo publishもビルド中に実行されていましたが、EAS Buildの場合、expo publishはビルド中には行われないみたいです。

https://docs.expo.io/build-reference/migrating/#no-more-automatic-publishing-before-building

```
With EAS Build, expo publish is not run as part of the build process. Instead, the JavaScript bundle is generated locally on EAS Build at build time and directly embedded in the app.
```

# EAS Build以外のローカルビルド方法

## ネイティブプロジェクトを生成して直接ビルドする

ネイティブプロジェクトを生成して直接ビルドする方法があります。EAS build --localと違って、ネイティブプロジェクトの編集が必要だったり、package.jsonが書き変わったりします。

### expo eject

Expo のManaged Workflowをやめて、iOSのネイティブプロジェクトをiosディレクトリに生成してます。Managed Workflowをやめるため、何度もejectする使い方は不向きです。

```
% expo eject
```

https://docs.expo.io/expokit/eject/

### expo prebuild

Expo SDK 41から導入された、iOSのネイティブプロジェクトをiosディレクトリに生成するコマンドです。できたばかりなので、まだ不安定かもしれません。

```
% expo prebuild --platform ios
```

https://docs.expo.io/workflow/customizing/

expo ejectと似ていますが、ejectの場合実行は１度の想定で、prebuildの場合何度も実行することを想定しているようです。(prebuildしてできたiosディレクトリは以下のファイルは、直接編集しない想定)

https://github.com/expo/fyi/blob/master/prebuilding.md#difference-between-eject-and-prebuild

> expo prebuild is very similar to expo eject, the core difference being that eject is intended to be run once, and prebuild can be used multiple times.

## Turtle CLI を使う

CI環境上でビルドするための従来ツールTurtle CLIがあります。eas build --local と同様、iOSのネイティブプロジェクトのディレクトリは残らないタイプです。

https://docs.expo.io/distribution/turtle-cli/

Turtle CLIの場合、Expoアカウントは必須でないですが、プロジェクトのpublishが必要になります(または、事前にexpo exportしたものをホスティングする)。

また、環境によるかもしれませんが、Expo SDKのダウンロードにかなり時間がかかります。