EASをGitHub Actionsで使ってReact Native(Expo)アプリをCIする
Expo Application Servicesが正式版になり、ビルドとストアへの登録(Submit)が無料プランでもできるようになりました。
今回はManaged WorkflowのExpoアプリをGitHub Actionsを使ってBuild & Submitする仕組みを作ってみました。
EASとはなんぞや、とかEAS Buildについてはこちらの記事を参考にさせてもらいました。
前提
対象はExpo Managed WorkflowのReact Nativeアプリです。
Bare Workflowでも試そうと思ったのですが、SDK 44へのアップデートで躓いてしまったので見送りました。
EAS Buildをローカルで実行する
GitHub Acrionsに組み込む前に、まずローカルで実行できるように設定が必要です。
1度ローカルでのBuild & Submitができるようになると、以降はCIでGitHub Acrionsなどから実行できるようになります。
まずはeas-cli
をインストールしましょう。
npm install -g eas-cli
インストールが終わったら、とりあえずビルドしてみます。
eas build
するとビルドするPlatformを確認されます。
? Select platform › - Use arrow-keys. Return to submit.
❯ All
Android
iOS
上下キーで対称を選んでEnterを押します。
その後、署名の設定などが必要になりますが、基本的にはデフォルトの設定値でEnterをバンバン打っていけばいけちゃいます。
一通り設定が終わると、ビルド待ちの状態になります。
正常に完了するのを待ちましょう。
うまくいかなかった場合は、ログなどを確認して修正します。
EAS Submitをローカルで実行する
ビルドが完了したら、続いてEAS Submit(Storeへの送信)を実施します。
こちらとりあえずコマンドを叩く・・・前に設定が必要なものがあります。
Google Play Storeにアクセスするためのサービスアカウントの作成と、手動によるStoreへのアップロードです。
(サービスアカウントの設定は、Expoアカウント毎に1回だけでOK。手動でのStoreへのアップロードはアプリ毎に1回実施が必要)
詳しくはこちらをみながらやってみてください。
サービスアカウントの設定ができたら、コマンドを実行します。
eas submit
またプラットフォームの選択が出るので選択します。
? Select platform › - Use arrow-keys. Return to submit.
❯ All
Android
iOS
あとはデフォルト設定でOK...のはずです。
うまくいかないところがあればコメントなどで教えてください。
各ストアでアプリがアップロードされていることを確認します。
(iOSならTest Flightなど、Androidならアーティファクトなど)
eas.jsonの修正
ローカルでのBuild & Submitが完了したら、GitHub Actionsで実行するための設定を追加します。
eas buildをした際に、eas.jsonが追加されていると思います。
そのファイルに、以下の部分を追加します。
{
"cli": {
"version": ">= 0.42.4"
},
"build": {
"development": {
"distribution": "internal",
"android": {
"gradleCommand": ":app:assembleDebug"
},
"ios": {
"buildConfiguration": "Debug"
}
},
"preview": {
"distribution": "internal"
},
"production": {}
},
"submit": {
"production": {
// 追加ここから
"ios": {
"ascAppId": "9999999999"
}
// 追加ここまで
}
}
}
ascAppId
にはAppStoreConnect -> 対称アプリの「一般」 -> 「App情報」 -> 「Apple ID」をコピーして設定します。
ひとまず設定するのはこれだけでOKです。
GitHub Actionsの設定ファイル追加
この辺を参考にGitHub Actions用の設定ファイルを作成して追加します。
今回は以下のようなファイルを追加しました。
name: EAS Build
on:
push:
branches:
- master
workflow_dispatch:
jobs:
build:
name: Install and build
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup Node.js
uses: actions/setup-node@v1
with:
node-version: 16.x
- name: Setup Expo
uses: expo/expo-github-action@v5
with:
expo-version: 4.x
expo-token: ${{ secrets.EXPO_TOKEN }}
expo-cache: true
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Build on EAS
run: npx eas-cli build --platform all --non-interactive --auto-submit --no-wait
masterブランチへのpushをトリガーにしたActionsです。
公式のサンプルから変えている箇所としては、yarnを使用しているところと、最後のStepで--auto-submit
を追加しているところです。
--auto-submit
を追加することで、BuildだけでなくそのままStoreへの登録まで実施しています。
(ちなみに公式ではyarnではなくnpmを推奨しているようです。)
SecretsにEXPO_TOKENを追加
eas-build.ymlファイルの中で、secrets.EXPO_TOKEN
を使っているためこれを設定します。
https://expo.dev/settings/access-tokensにアクセスし、Access tokenをCreateします。
作成したTokenを、GitHubのSecretsに登録します。
あとはmasterへPushしてやれば、自動的にActionが実行されて、Build & Submitが実施されます。
少し高度なビルド
シンプルなビルド構成のアプリであれば、以上のようにとても簡単にCIの構築が可能です。
しかし、ちょっと高度なビルドが必要な場合はもう一手間加える必要があります。
例えば以下のような場合です。
- 環境毎にビルド設定を変えてビルドさせ、内部テスト向けに使用したい。
- 一つのアプリリポジトリから2つのアプリを作成したい(有償版・無償版など)。
こういった場合、そもそもapp.json
ではやりきれないので、app.config.js
を使用している場合もあると思います。
自分が開発している「Fast Template」というアプリも、有償版と無償版があり、一部設定を切り替えてビルドしたいため、以下のようなapp.config.js
ファイルを利用しています。
const commonConfig = {
name: "Fast Template",
platforms: [
"ios",
"android"
],
version: "2.4.0",
orientation: "portrait",
splash: {
image: "./assets/images/splash.png",
resizeMode: "contain",
backgroundColor: "#4472c4"
},
assetBundlePatterns: [
"**/*"
],
locales: {
ja: "./locales/ja.json",
en: "./locales/en.json"
},
ios: {
buildNumber: "1",
supportsTablet: true,
userInterfaceStyle: "automatic",
appStoreUrl: "https://apps.apple.com/jp/app/%E3%82%BB%E3%83%AF%E3%82%B7%E3%82%BF/id1482524590"
},
android: {
versionCode: 2040001,
playStoreUrl: "https://play.google.com/store/apps/details?id=com.apps.hal.fasttemplate"
}
};
module.exports = () => {
if (process.env.APP_ENV === "standard") {
return {
...commonConfig,
slug: "fast-template",
icon: "./assets/images/icon.png",
ios: {
...commonConfig.ios,
bundleIdentifier: "com.apps.hal.fasttemplate"
},
android: {
...commonConfig.android,
package: "com.apps.hal.fasttemplate"
},
extra: {
eas: {
projectId: "d71783ad-7177-4fd1-af65-bf1ba0ace071"
}
}
};
} else if (process.env.APP_ENV === "free") {
return {
...commonConfig,
slug: "fast-template-free",
icon: "./assets/images/icon_free.png",
ios: {
...commonConfig.ios,
bundleIdentifier: "com.apps.hal.fasttemplatefree"
},
android: {
...commonConfig.android,
package: "com.apps.hal.fasttemplatefree"
},
extra: {
eas: {
projectId: "4650513e-d9ea-4511-98d5-f034e9bc6b6a"
}
}
};
}
};
process.env.APP_ENV
に応じてアプリ設定が切り替わり、アイコンやidが変わるような仕組みです。
これに対応するために、eas.json
を変更します。
{
"cli": {
"version": ">= 0.42.4"
},
"build": {
"development": {
"developmentClient": true,
"distribution": "internal"
},
"preview": {
"distribution": "internal"
},
"production_free": {
"env": {
"APP_ENV": "free"
}
},
"production_standard": {
"env": {
"APP_ENV": "standard"
}
}
},
"submit": {
"production_free": {
"ios": {
"ascAppId": "1496508495"
}
},
"production_standard": {
"ios": {
"ascAppId": "1496475554"
}
}
}
}
まぁ、修正したのはproduction
をproduction_free
とproduction_standard
に分けて、それぞれのenvを設定しただけですね。
これでapp.config.js
の実行時にAPP_ENV
を使用してビルドしてくれます。
また、submitの方もproduction
を分けて、それぞれのアプリにあったascAppId
を設定しました。
さらにGitHub Actionsの設定ファイルも変更します。
name: EAS Build
on:
push:
branches:
- master
workflow_dispatch:
jobs:
build:
name: Install and build
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup Node.js
uses: actions/setup-node@v1
with:
node-version: 16.x
- name: Setup Expo
uses: expo/expo-github-action@v5
with:
expo-version: 4.x
expo-token: ${{ secrets.EXPO_TOKEN }}
expo-cache: true
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Build on EAS(free)
run: npx eas-cli build --platform all --non-interactive --profile=production_free --auto-submit-with-profile=production_free
- name: Build on EAS(standard)
run: npx eas-cli build --platform all --non-interactive --profile=production_standard --auto-submit-with-profile=production_standard
変更したのは最後のeas-cli build
のパラメータですね。
--profile
と--auto-submit-with-profile
を使用して、それぞれの対応するprofileを使用してもらうようにしています。
これで、1つのリポジトリへの変更から2バージョンのアプリをビルドしてストアに登録することができます。
EAS Build & SubmitをGitHub Actionsで利用する利点
今回EAS Build & SubmitをGitHub Actionsで利用してみて、こんなところが良かったなぁと思いました。
1、煩雑なビルド・Store登録という作業がなくなる
一つは当たり前ですが自動化したため楽になった、という点です。
特にReact Nativeアプリの場合、iOSとAndroidの両方向けにアプリを作成することが多いと思いますが、そうすると当然ビルドやStoreへの登録作業も2倍発生します。
ここがGitHub Actionsによって指定のブランチへのPush(マージ)作業のみで済んでしまうというのはとても便利です。
自分の場合、iOS向けの登録作業をして満足してしまい、Android向けのアプリを更新し忘れた、みたいなことがしょっちゅう発生するので、それを防止できるのも助かります。
また、高度な(というほどでもないですが)ビルドができることで、2バージョン作る作業もなくなるのでとても便利です。
2、自動化にかかるコストを下げられる
自動化にかかるコストとして、細かくいうと「自動化するためのコスト」と「自動化を継続するコスト」の二つがあると思います。
まず、「自動化するためのコスト」については、EASというExpo(React Native)に特化したビルドサービスなので、非常に簡単、というところがあります。
今回の自動化の対応も、Managed Workflowについてはほとんど時間がかからず実現できてしまいました。
(Bare WorkflowについてはEASではない別の問題で実施できてません)
手軽に導入できるので自動化するためのコストを下げることができました。
また、「自動化を継続するコスト」としては、EASの利用自体は現状無料で利用することが可能です。
並列ビルドができないとか、ビルドタイムアウトの制約などはありますが、十分実用レベルの設定値になっています。
GitHub Actions側も、無料枠が2000分ついています。
しかも、これまでGitHub ActionsでReact Nativeアプリをビルドしようと思った時、Macインスタンスは10倍コストがかかるため200分しか利用できなかったわけですが、GitHub Actions + EASの場合、GitHub Actions側の実行はLinuxインスタンスで実行が可能なので、2000分丸々利用することができます。
これにより、十分に無料枠内でも利用できる自動化が可能となりました。
これは自分のような個人開発者にとってもとても嬉しいことではないかと思います。
(もちろん、将来的にEASの発展に貢献するため課金したいところではありますが・・・)
今後やりたいこと
とりあえず今回は、ビルド〜Storeへの登録までを自動化しました。
本当はさらにRelease ChannelやExpo-Updateも活用していきたいと思っています。
また、Bare Workflowでの確認ができていないので、そちらもやりたいですね。
最後までご覧いただきありがとうございました。
内容の不明点・誤りなどあれば、コメントやTwitterなどで指摘いただけると助かります。
Discussion