ClaudeExpoNov 13, 2025, 5:15 PM

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

A condensed section focused on the key takeaways first.

Original Post

Quick Digest

Summary

A condensed section focused on the key takeaways first.

claudeenmodel: claude-sonnet-4-20250514

Integrating EAS Workflows with GitHub Actions for Enhanced Mobile CI/CD

Key Points

  • Replace complex mobile signing setup with single EXPO_TOKEN
  • Conditional builds using fingerprint detection for native changes
  • Fast E2E testing with repack jobs for JavaScript-only updates

Summary

EAS Workflows is a CI/CD service purpose-built for React Native apps that can be used alongside or instead of GitHub Actions. It simplifies mobile app builds by handling complex signing processes and providing built-in jobs optimized for Expo projects.

Key Points

  • Simplified Configuration: Replace complex GitHub Actions with signing credentials and Fastlane setup with a single EXPO_TOKEN
  • Native Code Detection: Use fingerprint jobs to conditionally build only when native code changes, otherwise deploy JavaScript-only updates
  • Fast E2E Testing: Combine fingerprint detection with repack jobs to speed up Maestro end-to-end tests by reusing existing builds
  • Hybrid Approach: Mix GitHub Actions for existing workflows with EAS Workflows for mobile-specific tasks
  • Built-in Mobile Jobs: Access specialized jobs like build, update, fingerprint, repack, and maestro with minimal YAML configuration
  • Optimized Infrastructure: Leverage fast M4 Pro workers and React Native-optimized build processes

Full Translation

Translations

A translation section that keeps the flow of the original article.

claudejamodel: claude-sonnet-4-20250514

より高速なモバイルCIのためのEAS WorkflowsとGitHub Actionsの統合方法

より高速なモバイルCIのためのEAS WorkflowsとGitHub Actionsの統合方法

EAS WorkflowsがGitHub Actionsを強化し、最も困難なモバイルタスクを処理することで、React Nativeチームがより高速にビルドし、よりクリーンなCI設定を維持できる方法をご紹介します。

開発者とEAS Workflowsについて話すとき、他のCI/CDソリューションの話題が必然的に出てきます。それは当然のことです!世の中には多くの強力な継続的インテグレーション・デリバリーサービスが存在します。多くの開発者はすでにそれらのサービスに投資しており、重要なプロセスを変更することは大きな決断です。

開発者が知りたいのは次の2つのことです:

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

GitHub Actionsは当然ながら頻繁に比較対象となります。EAS Workflows自体にはGitHubアプリ統合があり、GitHub Actionsがサポートするのと同じGitHubイベント(プルリクエストの更新、ブランチのマージなど)からワークフローを実行できます。EAS Workflowsを検討している場合、すでにGitHub Actionsを試したことがあるか、本番環境で多くのアクションを実行している可能性が高いでしょう。

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を使用しながら、すでにうまく機能しているものはGitHub Actionsで継続実行するという境界線を曖昧にしていきます。

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

mainブランチにPRをマージするたびに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ビルドとiOSビルドを1つずつ作成しようとしているだけです。しかし、事態は急速にエスカレートします!

おそらく驚くことではありませんが、複雑さの多くは署名認証情報を中心としています。Androidキーストア用にいくつかの環境変数を設定する必要があります。Fastlane Matchを介してApple署名ステップを簡素化したとしても、それは完全に別のFastlane 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分数の観点で最も効率的なルートです。ただし、ビルドの完了を待ってから、GitHub Action内で結果を処理するカスタムスクリプトを実行することもできます:

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"

これはEAS Buildも使用しながらGitHub Action CI分数を使用しますが、設定と保守が少なく、EAS Buildが高速なM4 ProワーカーなどでReact Nativeビルド速度に最適化されているという事実を考慮すると、これはしばしば価値のあるトレードオフになります。

EAS Workflowsへの完全移行

この時点で、私たちのGitHub Actionが提供しているのは、mainブランチへのプッシュでビルドをトリガーする機能だけです。リポジトリが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

これで、ボットユーザーを作成して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エンドツーエンドテストの実行(maestro)などを行う組み込みジョブがあります。わずか数行のYAMLで、テストとデプロイメントを高速化する強力なワークフローを構築できます。

ビルドまたはアップデート

私たちのアプリが他の多くのReact Nativeアプリと同様で、平均的なプルリクエストがネイティブコードよりもJavaScriptのみに変更を加える可能性がはるかに高いとします。現在のワークフローは、実際にネイティブコードを変更するコミットがごくわずかである可能性が高いにもかかわらず、mainへのすべてのコミットで完全なビルドを実行するように設定されています。

ネイティブフィンガープリントを使用してネイティブコードが変更されたタイミングを判断し、それが真の場合にのみビルドするように修正しましょう:

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
  ## repeat the get build -> update / build steps for iOS

完全なFingerprintワークフローはこちらをご覧ください。

EASにはすでに最近の以前のビルドとフィンガープリント(すべてのビルドで計算される)があるため、このデータはEAS Workflow内ですでに利用可能です。お好みの方法で点と点を結ぶだけです。

フィンガープリントとrepackによる高速E2Eテスト

同じ原理(ネイティブフィンガープリントに基づく条件付きビルド)を使用して、エンドツーエンドテストを高速化できます。以前は、テスト実行に加えて完全なビルドが必要なため、エンドツーエンドテストをいつ、どこで実行するかを制限することを選択していたかもしれません。理想的には、ユニットテスト、リンティング、その他のチェックと同様に、プルリクエストを作成または更新するたびに実行したいでしょう。

フィンガープリントを使用すると、一致するビルドがすでに存在する場合にビルドの代わりにRepackを実行することで、Maestroエンドツーエンドテストのビルドフェーズを高速化できます。repackは「ビルド時アップデート」のようなものと考えることができます。repackジョブは既存のビルドを取得し、JavaScriptを再バンドルし、古いビルドのJavaScriptバンドルを新しいものに置き換え、再署名します。これにより、わずか1分でテスト実行用の新しいビルドを取得できます。