OpenAIExpo2025/11/13 17:15

How to integrate EAS Workflows with GitHub Actions for faster mobile CI

要点だけを先に読めるように短く再構成したセクションです。

元記事

Quick Digest

要約

要点だけを先に読めるように短く再構成したセクションです。

openaijamodel: gpt-5-mini-2025-08-07

EAS Workflows と GitHub Actions を統合してモバイル CI を高速化

Key Points

  • 署名管理を簡素化
  • ネイティブ差分で条件ビルド
  • repackで高速E2E

Summary

この記事は、既存の GitHub Actions と EAS Workflows を組み合わせるパターンと、段階的な移行で得られる実務的な利点をまとめたものです。主な効果は署名管理の簡素化、CI 時間の節約、iOS 用 macOS ランナー不要化、そして fingerprint / repack による条件ビルドでの高速な E2E テスト実行です。

Key Points

  • 統合パターン
    • GitHub Actions から npx eas build を呼び出す(短期導入、--no-wait で CI 分を節約、--json で結果取得可能)。
    • Expo GitHub App 経由で EAS Workflows に直接トリガーを移行(完全移管、YAML が短く管理が容易)。
  • 署名管理の簡素化: 複数の keystore/Fastlane 変数を EXPO_TOKEN / EAS の資格情報に集約可能。
  • 無駄なビルドを減らす: fingerprint でネイティブ差分を検出し、既存ビルドがあれば repack で高速に JS を差し替えてテストを実行。
  • ビルトインジョブ: build/update/submit/fingerprint/repack/maestro などで短い YAML で強力なパイプラインを作成。
  • 混在運用が可能: 既存の Actions(lint、unit tests 等)は継続しつつ、ビルド・署名・E2E を EAS に委任して段階的に移行。

Practical tips

  • CI 時間を節約するには: npx eas build --platform all --profile preview --non-interactive --no-wait を使ってビルドを非同期で開始する。
  • ビルド結果を Action 内で処理する場合: npx eas build --json の出力を解析して buildUrl を取得し、後続ジョブで利用する。
  • iOS ビルドのために GitHub の macOS ランナーを維持する必要はほぼない(EAS 上で実行されるため)。
  • 段階的移行推奨: まず eas build 呼び出しで運用を簡素化→fingerprint/repack を導入して不要ビルドを削減→必要に応じて Workflows に完全移行。

Full Translation

翻訳

原文の流れを保ったまま読める翻訳セクションです。

openaijamodel: gpt-5-mini-2025-08-07

EAS WorkflowsをGitHub Actionsと統合してモバイルCIを高速化する方法

Product • Development • React Native • 2025年11月13日 • 読了10分 Keith Kurak Engineering

EAS WorkflowsがGitHub Actionsをどのように補完し、React Nativeチームがより高速にビルドし、よりクリーンなCI構成を維持できるようにするかを説明します。

概要

EAS Workflowsについて開発者と話すと、他のCI/CDソリューションが必ず話題にのぼります。既に多くの強力なCI/CDサービスが存在し、多くの開発チームはそれらに投資済みであるため、重要なプロセスを変更するのは大きな決断です。開発者が知りたいのは主に2点です。

  • EAS Workflowsは他のソリューションと何が違うのか?
  • 既存のCIランナーにEAS Workflowsをどう統合できるのか?

GitHub Actionsは自然に比較対象になります。EAS Workflows自体はGitHubアプリ統合を持ち、GitHub Actionsと同じGitHubイベント(例:プルリクエストの更新、ブランチのマージ)からワークフローを実行できます。既にGitHub Actionsを試している、あるいは本番で多数のactionsを稼働させている場合、EAS Workflowsの導入を検討している可能性が高いでしょう。

EAS WorkflowsはReact Nativeアプリ向けに設計されたCI/CDサービスで、EAS Build、Update、Submitと相性の良い組み込みジョブを最小限の設定で提供します。多くのモバイル中心のプロセスでは他のCIソリューションの代替として使えますが、同時に併用することも可能です。現在のCI/CDをWorkflowsと組み合わせることで、署名処理を簡素化し、ビルドを高速化し、重要なジョブだけを実行することで、最終的により高速で柔軟、かつ保守しやすいパイプラインを作成できます。

ここではEAS Workflowsが提供するものを理解し、GitHub Actionsと比較/対比しつつ統合方法を見ていきます。まずReact Nativeアプリ用の簡単なGitHub Actionsから始め、その等価物がEAS Workflowsでどうなるかを見て、次に両者を混在させながらWorkflowsを最も有用な箇所で使い続ける方法を示します。

シンプルなビルドワークフロー

PRをmainにマージするたびにAndroidとiOSのプレビュービルドを作成してQAが変更をテストできるようにしたいとします。これはGitHubイベントに基づいてトリガーする必要があり、既にGitHub Actionsを使えるので、まずは単純なGitHub Actionを書くのが合理的です。

name : Build iOS and Android on merge to main
on :
  push :
    branches :
      - main
jobs :
  build-android :
    name : Android Release Build
    runs-on : ubuntu - latest
    env :
      # Signing secrets (configure in repo Settings > Secrets and variables > Actions)
      ANDROID_KEYSTORE_BASE64 : $ { { secrets.ANDROID_KEYSTORE_BASE64 } }
      ANDROID_KEYSTORE_PASSWORD : $ { { secrets.ANDROID_KEYSTORE_PASSWORD } }
      ANDROID_KEY_ALIAS : $ { { secrets.ANDROID_KEY_ALIAS } }
      ANDROID_KEY_PASSWORD : $ { { secrets.ANDROID_KEY_PASSWORD } }
    steps :
      - name : Checkout repo
        uses : actions/checkout@v4
      - name : Setup Bun
        uses : oven - sh/setup - bun@v1
        with :
          bun-version : latest
      - name : Decode Android keystore
        run : |
          echo "$ANDROID_KEYSTORE_BASE64" | base64 --decode > android/release.keystore
      - name : Write signing config gradle.properties
        run : |
          cat >> android/gradle.properties <<EOF
          MYAPP_UPLOAD_STORE_FILE=release.keystore
          MYAPP_UPLOAD_KEY_ALIAS=${ANDROID_KEY_ALIAS}
          MYAPP_UPLOAD_STORE_PASSWORD=${ANDROID_KEYSTORE_PASSWORD}
          MYAPP_UPLOAD_KEY_PASSWORD=${ANDROID_KEY_PASSWORD}
          EOF
      - name : Set up JDK
        uses : actions/setup - java@v4
        with :
          distribution : temurin
          java-version : "17"
      - name : Set up Android SDK
        uses : android - actions/setup - android@v3
      - name : Assemble Release APK/AAB
        working-directory : android
        run : |
          android/gradlew :app:bundleRelease --stacktrace
      - name : Upload Android artifact
        uses : actions/upload - artifact@v4
        with :
          name : android - release - aab
          path : android/app/build/outputs/bundle/release/ *.aab
  build-ios :
    name : iOS Release Build
    runs-on : macos - latest
    needs : build-android
    env :
      # You can set this if your Fastlane expects env vars
      MATCH_PASSWORD : $ { { secrets.MATCH_PASSWORD } }
      APPLE_ID : $ { { secrets.APPLE_ID } }
      APP_STORE_CONNECT_API_KEY_ID : $ { { secrets.APP_STORE_CONNECT_API_KEY_ID } }
      APP_STORE_CONNECT_API_ISSUER_ID : $ { { secrets.APP_STORE_CONNECT_API_ISSUER_ID } }
      APP_STORE_CONNECT_API_KEY_BASE64 : $ { { secrets.APP_STORE_CONNECT_API_KEY_BASE64 } }
    steps :
      - name : Checkout repo
        uses : actions/checkout@v4
      - name : Setup Bun
        uses : oven - sh/setup - bun@v1
        with :
          bun-version : latest
      - name : setup - cocoapods
        uses : maxim - lobanov/setup - cocoapods@v1
        with :
          podfile-path : myApp/Podfile.lock
      # Pull signing certs / profiles with match
      - name : Fetch signing assets with fastlane match
        working-directory : ios
        env :
          MATCH_PASSWORD : $ { { secrets.MATCH_PASSWORD } }
          APPLE_ID : $ { { secrets.APPLE_ID } }
        run : |
          bundle exec fastlane match appstore --readonly
      # Build the ipa (you can wrap this in a lane like `fastlane ios build`)
      - name : Build iOS archive / ipa
        working-directory : ios
        env :
          # If your Fastlane lane needs these:
          APP_STORE_CONNECT_API_KEY_ID : $ { { secrets.APP_STORE_CONNECT_API_KEY_ID } }
          APP_STORE_CONNECT_API_ISSUER_ID : $ { { secrets.APP_STORE_CONNECT_API_ISSUER_ID } }
        run : |
          bundle exec fastlane ios build
      - name : Upload iOS artifact
        uses : actions/upload - artifact@v4
        with :
          name : ios - release - ipa
          path : ios/build/ *.ipa

このやり方自体は複雑ではないように見えますが、すぐに複雑さが増します。驚くことではありませんが、多くの複雑さは署名認証情報まわりに集中します。Androidキーストア用に複数の環境変数を設定する必要があり、Apple署名をFastlane Matchで簡略化したとしても、Match用の別リポジトリを用意する必要があります。

少しEAS Buildを加える

長い間、開発チームはGitHub ActionsからEAS Buildを呼び出してきました(Expoチーム自身もその例があります)。GradleやFastlaneのステップをEAS Buildの呼び出しに置き換えてみましょう:

name : EAS Build on merge to main
on :
  push :
    branches : [ main ]
jobs :
  trigger-eas-builds :
    runs-on : ubuntu - latest
    env :
      EXPO_TOKEN : $ { { secrets.EXPO_TOKEN } }
    steps :
      - uses : actions/checkout@v4
      - name : Setup Node & Bun
        uses : actions/setup - node@v4
        with :
          node-version : "20"
      - run : |
          bun install
      # Fire-and-forget Android and iOS build
      - name : Kick off Android and iOS builds
        run : |
          npx eas build \
            --platform all \
            --profile preview \
            --non-interactive \
            --no-wait

この方がはるかに短くなります。EAS BuildはExpoプロジェクトに典型的なprebuild、Gradle、Fastlaneなどのコマンドをすでに適切に設定済みです。しかし、複雑さの大きな削減はビルド認証情報の管理にあります。複数の環境変数やFastlane Match設定を、EAS Buildを呼び出すための単一のEXPO_TOKENに置き換えました。ビルド認証情報はすでにEASに保存されており、eas credentialsでアップロードするか、手動で初回ビルドを呼び出すと自動生成できます。

上記のシンプルなワークフローはEAS Buildを起動して終了します(ビルドが完了するのを待ちません)。これはCI分数の観点で最も効率的な方法です。ただし、ビルド完了を待ってからカスタムスクリプトで結果を処理することもできます:

npx eas build --platform android --profile preview --json > build.json
BUILD_URL=$(jq -r '. [0].artifacts.buildUrl' build.json)
echo "Android build URL : $BUILD_URL"

この方法はGitHub ActionsのCI分数を消費しますが、設定と保守の少なさ、EAS Buildがfast M4 Proワーカー等でReact Nativeビルド速度を最適化している点を考えると、しばしば有益なトレードオフになります。

EAS Workflowsへの完全移行

この時点で、我々のGitHub Actionが行っているのはmainブランチへのpushでビルドをトリガーすることだけです。リポジトリをExpoのGitHubアプリに接続すれば、同じトリガーをEAS Workflowに追加でき、GitHub Actionsを単なる中継として使う必要がなくなります:

name : Build preview on main push
on :
  push :
    branches : [ main ]
jobs :
  build_ios :
    type : build
    params :
      platform : ios
      profile : preview
  build_android :
    type : build
    params :
      platform : android
      profile : preview

これにより、Botユーザーを作成してEXPO_TOKENを渡す必要や、別サービス上で追加のCI分数を消費する必要がなくなります。iOSビルド自体はEAS上で行われるため、GitHub ActionsでMacランナーを使う必要もありません。

Expo最適化された強力なワークフロー

ビルドを行うためのEAS Workflow YAMLは短くシンプルですが、GitHub Actionsからビルドを呼び出すこと自体が大幅に複雑だったわけではありません。EAS Workflowsが本当に得意とするのは、Expoアプリ固有の特性に基づいてジョブを条件付きで実行する必要がある場合です。

基本的なbuildやupdateジョブに加えて、EAS Workflowsには以下のような組み込みジョブがあります:

  • ネイティブ構成を一意に識別するハッシュを生成する(fingerprint)
  • 既存のビルドに新しいJavaScriptを適用する(repack)
  • シミュレータ上でMaestroのE2Eテストを実行する(maestro)

数行のYAMLだけで強力なワークフローを構築し、テストやデプロイを高速化できます。

BuildかUpdateか

多くのReact Nativeアプリと同様に、プルリクエストの大半はネイティブコードではなくJavaScriptの変更であることが多いです。現在のワークフローがmainへのすべてのコミットでフルビルドを行っているなら、native fingerprintを使ってネイティブコードが変更された場合にのみビルドするように改善できます:

name : Build or update preview version
on :
  push :
    branches : [ main ]
jobs :
  fingerprint :
    # calculate fingerprint
    environment : preview
    type : fingerprint
  android_get_build :
    # if there's a matching build...
    needs : [ fingerprint ]
    type : get - build
    params :
      fingerprint_hash : $ { { needs.fingerprint.outputs.android_fingerprint_hash } }
      platform : android
      profile : preview
  android_update :
    # publish an update to that build
    needs : [ android_get_build ]
    if : $ { { needs.android_get_build.outputs.build_id } }
    type : update
    params :
      channel : preview
      platform : android
  android_build :
    # otherwise make a new build
    needs : [ android_get_build ]
    if : $ { { !needs.android_get_build.outputs.build_id } }
    type : build
    params :
      platform : android
      profile : preview

iOSについても同様に get build -> update / build のステップを繰り返します

EASは直近のビルドとfingerprint(各ビルドで計算される)を既に持っているため、このデータはEAS Workflow内で利用可能です。あとは望む形でそれらをつなぐだけです。

fingerprintとrepackによる高速E2Eテスト

同じ原則(ネイティブfingerprintに基づいた条件付きビルド)をE2Eテストの高速化にも使えます。E2Eテストはフルビルドに加えてテスト実行が必要なため、以前は実行場所やタイミングを制限していたかもしれません。本来はユニットテストやlintと同様に、PRごとに実行したいはずです。

fingerprintを使えば、既にマッチするビルドがある場合にフルビルドの代わりにRepackを実行してMaestroのE2Eテスト用のビルド段階を高速化できます。Repackは"ビルド時のアップデート"のように考えられます。repackジョブは既存のビルドを取り、JavaScriptを再バンドルし、古いビルド内のJavaScriptバンドルを新しいものに置き換え、再署名します。これにより、テスト実行用の新しいビルドを約1分で...