1.1.3 · 開発基盤

EAS Update の安全な運用 — stg / prd 分離

2026.07.11約6分

00Overview

Tools

  • EAS UpdateOTA 配信Free
  • GitHub Actionsstg の自動配信Free
  • Expo Gostg の実機確認Free

01Story

Situation

1.1.11.1.2 で、開発ループとリリースの仕組みは通った。main にマージすると OTA が配信される。まず最初のリリースまでは、この構成で足りる。

Complication

問題は、リリースした後の改修フェーズである。一般的な構成はこうなっている。

main にマージ
      ↓
  EAS Update
      ↓
  公開ユーザー

この構成だと、デバッグコードや一時的な修正が誤って main に入ったとき、そのまま公開ユーザーへ OTA が届いてしまう。さらに、ネイティブコードを変更した後でも OTA は配信できるため、古いアプリへ互換性のない JavaScript が降ってクラッシュする危険もある。

Question

開発中のコードや、ネイティブ非互換な更新を、公開ユーザーへ誤って届けないようにするには?

02Solution

Criteria

  • 開発中のコードが公開ユーザーへ届かないこと
  • ネイティブ変更で古いアプリがクラッシュしないこと
  • Expo Go による開発体験を維持できること
  • 公開タイミングを自分で制御できること

Answer

出発点は、OTA が届く条件を正確に押さえることである。OTA が配信されるには、次の 2 つの条件を同時に満たす必要がある。

channel が一致
      AND
runtimeVersion が一致
  • channel … 配信先 (開発用か、公開用か)
  • runtimeVersion … ネイティブ互換性

両方が一致した端末だけが OTA を受信する。逆に言えば、どちらか片方をずらせば、その端末には届かない。この性質を使って、2 つの経路を分離した。

channel で「配信先」を分ける。 LycoApp では配信チャネルを stg と prd の 2 つに分けている。

channel用途配信方法
stg開発・検証GitHub Actions が自動配信
prd公開ユーザー手動配信

main へのマージでは stg にだけ自動配信され、prd への配信は明示的に実行しない限り起きない。これで、開発中のコードが公開ユーザーへ流れる経路が構造的に塞がれる。

runtimeVersion で「ネイティブ互換性」を守る。 runtimeVersion は appVersion ポリシーを採用している。

{
  "runtimeVersion": {
    "policy": "appVersion"
  }
}

こうすると version 1.0.0 のアプリの runtimeVersion は 1.0.0 になる。ネイティブコードを変えたときは version を 1.0.1 に上げる。すると runtimeVersion も自動で 1.0.1 に変わるため、1.0.0 のアプリには 1.0.1 の OTA が届かない。ネイティブ変更による非互換クラッシュを、バージョンを上げるという普段の操作だけで防げる。

Expo Go だけは例外を挟む。 Expo Go は runtimeVersion = exposdk:<SDK Version> しか認識できない。そこで GitHub Actions では、stg 配信のときだけ policy を一時的に sdkVersion へ切り替えて OTA を配信する。

{
  "runtimeVersion": {
    "policy": "sdkVersion"
  }
}

この書き換えは CI 上だけで行い、Git にはコミットしない。ストアビルドと prd への OTA では、通常どおり appVersion のままである。開発体験 (Expo Go) と本番の安全性 (appVersion) を、両立させるための小さな仕掛けである。

Reason

注意力ではなく、仕組みで防ぎたかったからである。 「本番に間違って配信しないよう気をつける」という運用は、いつか必ず事故る。人はミスをする。だから OTA が届く条件そのものに手を入れた。channel をずらせば公開ユーザーには届かず、runtimeVersion をずらせば古いアプリには届かない。届く条件を握っておけば、届いてはいけないものは、そもそも届かない。これが一番安心して眠れる設計だと考えている。

Options

  • 単一 channel のまま運用する — main → EAS Update → 公開ユーザー、という素の構成。設定は最もシンプルだが、誤配信とネイティブ非互換クラッシュのリスクをそのまま抱える。最初のリリースまではこれでよいが、改修フェーズに入る前に分離した。

03Result

Good

日常の開発フローは、こう落ち着いた。

修正
 ↓
main にマージ
 ↓
GitHub Actions
 ↓
stg へ自動 OTA
 ↓
Expo Go で確認

公開するときだけ、GitHub Actions から prd の EAS Update を手動で実行する。結果として、開発中のコードは公開ユーザーへ届かず、ネイティブ変更によるクラッシュも防げ、Expo Go の快適な開発体験も残り、公開のタイミングも自分で握れる。狙った 4 つが、すべて成立した。

Bad

代わりに、ひと手間と分かりにくさが増える。prd は手動配信なので、公開のたびに自分で実行する必要がある。また、CI 上で runtimeVersion を一時的に書き換える仕掛けは、仕組みを知らないと「なぜここだけ policy が違うのか」と混乱しやすい。この分かりにくさを埋めるために、こうしてドキュメントに残している。

Follow-up

この stg / prd 分離は、一度組めば他のアプリでもそのまま使える。だから Git のテンプレートリポジトリにして、新規アプリはコピーして始める 運用にした。最初のリリースは 1.1.11.1.2 の素の構成で済ませ、リリースが済んだらこの分離を入れてテンプレ化しておく。以降のアプリは、最初から安全な OTA 運用の上でスタートできる。