Open37

実践EAS Update 調査から方針を決めるまで

はらだはらだ

updateコマンドではブランチを指定

eas update --branch [branch] --message [message]
はらだはらだ

ブランチを見てみる(productionをeas channel:create productionで作っておいた)

eas branch:list
出力

Channel:
Name  production
ID    hoge

Branches pointed at this channel and their most recent update group:

チャネルを見てみる(productionを作っておいた)

eas channel:list
出力
Branches:

Branch           production
Platforms        N/A
Runtime Version  N/A
Message          N/A
Group ID         N/A
はらだはらだ

ステージングのchannelとbranchも作る

eas channel:create staging
出力
Channel:
Name  staging
ID    foo

Branches pointed at this channel and their most recent update group:

No branches are pointed to this channel.

———

Channel:
Name  production
ID    hoge

Branches pointed at this channel and their most recent update group:

ブランチも作られた

出力
Branches:

Branch           staging
Platforms        N/A
Runtime Version  N/A
Message          N/A
Group ID         N/A

———

Branch           production
Platforms        N/A
Runtime Version  N/A
Message          N/A
Group ID         N/A
はらだはらだ

ステージングブランチにupdateしてみる

eas update --branch staging
はらだはらだ
出力
{
  "updates": {
    "url": "https://u.expo.dev/bar"
  }
}


Cannot automatically write to dynamic config at: app.config.ts
    Error: update command failed.

と出た(barにはexpo のプロジェクトIDが入る)のでapp.jsonに追記した

はらだはらだ

もう一度updateすると
Provide an update message とメッセージの入力が求められる。
デフォルトは最終コミットメッセージ

はらだはらだ

updateは数分で終わった。

出力
Channel: staging pointed at branch: staging

みたいなログも出るので、channelとbranchを確認できる。
expo.devにのUpdatesにも表示された。

はらだはらだ

eas.jsonの各profileにchannelを追加しreleaseChannelを削除
今までreleaseChannelの値で制御指定していたものを、環境変数のAPP_VARIANTで制御するように変更
build時はeas.jsonのenvからAPP_VARIANTを取得し、update時は

APP_VARIANT="prod" eas update --branch production

のように指定することで取得できるようにする
https://docs.expo.dev/build-reference/variables/#can-i-share-environment-variables-defined-in-easjson-with-expo-start-and-eas-update

はらだはらだ

channelは

import * as Updates from 'expo-updates';
Updates.channel

で取得可能だが、app.config.jsonでは起動前だから上記のexpo-updatesを使った取得はできなさそう?(ドキュメントにもapp.config.jsonではexpo-updatesから取得していない)

はらだはらだ

また、app.config.jsonのexpo.extraにも変数を埋め込むことが可能

export default () => ({
  expo: {
    extra: {
      API_URL: process.env.API_URL || null,
    },
    // ...
  },
});

これはexpo-constantsで

Constants.expoConfig.extra.API_URL

で取得可能

expo-updatesでも

Updates.manifest?.extra?.expoClient?.extra?.eas?.API_URL

で取得できるが、dev環境、もしくはUpdataがないとUpdates.manifestがundefinedなので微妙
https://docs.expo.dev/eas-update/environment-variables/#setting-and-getting-environment-variables-when-publishing-an-update

はらだはらだ

横道に逸れまくったが、
build時: eas.jsonのenvからAPP_VARIANTを取得
update時: コマンドの環境変数指定からAPP_VARIANTを取得
が良さそう

はらだはらだ

APP_VARIANTとブランチの対応関係を間違えると悲惨(APIエンドポイントに開発環境を指定したものをユーザーにデリバリーしてしまったり)なので、package.jsonのscriptsにエイリアスを書いておく。

"eas-update-dev": "APP_VARIANT=stg eas update --branch staging",
"eas-update-prod": "APP_VARIANT=prod eas update --branch production",
はらだはらだ

例えば

 "APP_VARIANT=prod eas update --branch production-preview"

でupdateして、それをproductionにつなげれば、かなり本番に近いテストができて安全…?

いやだめか。
productionチャネル がproduction-previewブランチを向いていると、次に試したいバージョンをproduction-previewブランチに上げてしまったらproductionが読み取ってしまう。
なので、どんどんブランチは繰り上げていかないといけないのか。

はらだはらだ
はらだはらだ

まずはstagingブランチの状態を確認

eas branch:view staging
出力
Branch:
Name  staging
ID    hoge

Recent update groups:

Platforms                 android, ios
Runtime Version           1.0.0
Message                   "add ? to runtimeVersion in mypage drawer menu" (4 minutes ago by n_harada)
Code Signing Key          N/A
Is Roll Back to Embedded  No
Group ID                  foo

———

Platforms                 android, ios
Runtime Version           1.0.0
Message                   "comment out eas.json ios build env macos-monterey-12.6-xcode-14.0 staging" (2 hours ago by n_harada)
Code Signing Key          N/A
Is Roll Back to Embedded  No
Group ID                  bar
はらだはらだ
eas update:republish --group bar

注) barは、上記のGroup ID(実際はuuid)

はらだはらだ

Update成功!…と思いきやログアウトされてしまった。
調べてみたら、API_HOSTが開発環境に変わってしまっている。
が、それはロールバック先のUpdateでのAPI_HOSTが開発環境のものだったから。ということで成功。

はらだはらだ

republishの際は環境変数指定はいらないっぽい。前にpublishしたupdateをそのまま反映させるなら当然だけど。
ロールバック簡単で助かる。クラシックupdateのときはロールバックが無限ループしてしまっていたから。

はらだはらだ
eas update:republish --branch staging

みたいなロールバックの仕方もあるらしいけど、ロールバック先のcommitを指定できない分、GroupID指定と比べると完全に下位互換な気がするけどどうなんだろう
と思ってやってみたら、過去のupdateの履歴が出てきた。選択できた。
どっちでもいいな。
手数はブランチ指定の方が少ないからブランチ指定するか。
ロールバックする時のことも考えてcommitメッセージ書かねば。

はらだはらだ

--autoオプションを指定

eas update --auto

すると、

  • gitのcommitのブランチと同名のブランチ名
  • メッセージは最終commitのメッセージ
    でupdateがpublishされる
eas update --branch staging --auto

とすれば、ブランチ名はstagingで、メッセージのみ自動化され、は最終commitのメッセージが指定される。
--autoをつけることでinteractiveでなくなるので、CI/CD環境ではこれを使う

はらだはらだ

このあとやること

はらだはらだ

あとは、今developでもmianと同じくbuild, submitまでおこなっているから、それはなしにしよう

mainでsubmitまで行なって、同時にupdateまでpublishすることにすると、storeからインストールしたあとには必ずupdateが走る?submitしたものとupdateしたものが同じだからupdateは走らない?

runtimeVersionが変わった時だけbuild + submitして、変わらなかったらupdateだけにしたい
いや変わらなくてもupdateタグがついてたらupdateするようにしたい(リファクタリングでupdateしたくないのと、タグをつけることで、runtimeVersionを変え忘れを防げそう)

はらだはらだ

GitHubActionsでの自動Submit, Update案

  • submit時
    commitにeas-submit-v1.0.0みたいなruntimeVersionを入れたタグをつける

  • update時
    commitにeas-update-v1.0.0.3みたいなruntimeVersion + update用のバージョンを入れたタグをつける

  • version1.0.0(パッチ番号まで)ないしversion1.0.0(パッチより下位が0)ならsubmit

  • version1.0.0.1(パッチより下位が1以上)ならupdate
    とかもありかな。ちょっと明示的じゃなくて良くないかな。

はらだはらだ

runtimeVersionのポリシーはappVersionにしよう
これは、プロジェクトのバージョン番号を元にランタイムバージョンが自動的に設定されるポリシー。
ネイティブのビルド番号(iOSのbuildNumber、AndroidのversionCode)は関係ない。
弊アプリでは

  • ネイティブコードに変更があったとき(submit時)のみプロジェクトのバージョン番号をインクリメント
  • ネイティブコードに変更はないが、submit後に修正を加えてsubmitしたいときにネイティブのビルド番号をインクリメント
    なのでちょうどいい。

なので、
ネイティブコードに変更 => プロジェクトのバージョン番号をインクリメント
の既存の流れだけで済む。

はらだはらだ

「ネイティブコードに変更があったけどプロジェクトのバージョン番号が変更されていない」は検出不可能だと思われるので、updateのタグをつけた時だけupdateを走らせることにする

はらだはらだ

デバッグ用に、commitにつけたタグもランタイム時に隠しコマンドで見られるようにしたいな
EASのbuildの制限くると嫌だから来月やろう

はらだはらだ

ブランチ戦略について

チャネルとブランチの対応関係を変更するには、

eas channel:edit production --branch version-2.0

のように、チャネルを特定のブランチに紐付けさせる(https://docs.expo.dev/eas-update/how-eas-update-works/#matching-updates-and-builds)

チャネルはユーザーの手元のアプリでは固定だから当然と言えば当然

はらだはらだ

なので、
stagingチャネルで新しいコード(v2ブランチ)を試し、
productionチャネルをv2ブランチに向ける
みたいな戦略がある。

はらだはらだ

https://zenn.dev/link/comments/4b6df53ebe3c03 と同じ話で、

  1. stagingチャネルでv1.0ブランチを試す
  2. v1.0に問題ないことを確認
  3. prodチャネルをv1.0に向ける
  4. stagingチャネルでv1.1ブランチを試す
  5. v1.1に問題ないことを確認
  6. prodチャネルをv1.1に向ける
    以下同様に続く

って感じか。どんどんブランチを繰り上げていく。

上記は前提としてruntimeVersionが同じ
もしruntimeVersioinがv2.0から変わるのであれば、

  1. prodチャネルとstagingチャネルでv2.0でビルドし直し
  2. stagingチャネルでv2.1を試す
  3. v2.1に問題ないことを確認
  4. prodチャネルをv2.1に向ける
    以下同様

って感じか

はらだはらだ

メリット

  • このフローは他のフローよりも安全である。
    更新は内部テスターに配布されるテストビルドでテストされるため、本番ビルドにデプロイされるアーティファクトはテストされた正確なものである。
  • GitHubブランチとEASアップデートブランチの間に直接的なマッピングを作成し、GitHubのコミットとEASアップデートの間にもマッピングを作成する。
  • GitHubブランチを追跡している場合、EASアップデートブランチを各GitHubブランチに作成し、それらのブランチをビルドのチャネルにリンクできる。
  • デプロイの以前のバージョンは常にGitHubに保存され、プロジェクトの以前のバージョンに簡単に戻ることができる

デメリット

  • 1つの本番ランタイムバージョンにつき1つのチャネルが必要
  • ランタイムバージョンポリシーの使用が困難になる。runtimeVersionが必然的にカスタムに。
  • ブランチ名のブックキーピングが必要
  • チームとのコミュニケーションで現在のテストビルドと本番ビルドにポイントされているブランチを伝える必要がある。
はらだはらだ

ここまでやるの結構めんどそうなので、production-previewみたいな、ブランチ名だけ異なるほぼproductionのステージング環境作って、そこで試したコードはproduction反映、とかでもいい気がする。
それがこれっぽい。
https://docs.expo.dev/eas-update/deployment-patterns/#persistent-staging-flow

デメリットは以下として挙げられてる

  • アプリの以前のバージョンのチェックアウトは、古いブランチではなく古いコミットをチェックアウトする必要があるため、少し複雑です。
  • productionにマージすると、stagingチャネルのビルドからproductionチャネルのビルドに移動する代わりに、更新が再ビルドされて再公開されます。

一つ目がどう複雑なのかわかってない。eas update:republishがめっちゃ簡単だったので。
二つ目はまぁ再ビルドされるよね、って感じ。そこに不確実性が紛れ込むのはあり得る。

はらだはらだ

このproduction-previewを取り入れるのであれば、staging環境(stagingのDB、APIサーバー)と繋げるstagingブランチいらない気もするな?

はらだはらだ

mainブランチへのPR作成時にproduction-previewをbuild、ないしupdateするのが良いなぁ

はらだはらだ

弊アプリのEAS Update方針まとめ

ランタイムバージョン

  • runtimeVersionポリシーはappVersionで、アプリのバージョン番号と常に一致

ブランチ戦略

EASブランチ EASチャネル APP_VARIANT Gitブランチとの関係
production production production mainへのマージでbuild + submit, update
production_preview production_preview production mainへのPRでbuild, update
staging staging staging developマージでbuild, update
local local local feature

ただし、

  • アップデート用タグ(eas-update-v1.0.0.1みたいなフォーマットのタグ)をつけたcommitのみ自動でupdate
  • サブミット用タグ(eas-submit-v1.0.0みたいなフォーマットのタグ)をつけたcommitのみ自動でbuild(submit)

その他

  • APP_VARIANTの指定を間違わないように、ブランチごとupdateコマンドのエイリアスをpackage.jsonに設定
  • ロールバックは今の所手作業
     eas update:republish --branch production