取り組みの背景 — 6本のアプリのリリース作業が回らなくなった日
2026年に入ってからの私は、壁紙系を中心とした6本のアプリの v2.0.0 / v2.1.0 アップデートを並行して進めていました。1本ずつ Xcode でアーカイブして App Store Connect にアップロードし、Android 側では AAB を書き出して Play Console に上げる。この手作業を6本分繰り返すと、検証も含めてほぼ2日が消えます。リリース後に小さな不具合が見つかれば、また同じ工程のやり直しです。
ビルドと配信は機械に任せて、人間は中身に集中するべきだ——2014年から個人開発を続けてきて、これほど痛感したことはありません。その答えとして導入したのが Expo Application Services(EAS)でした。クラウド上で iOS / Android のビルドを行い、OTA(Over-The-Air)配信や CI/CD 連携までを一つのサービスで担ってくれます。手元に Mac がなくても iOS アプリをビルドできる点も、環境構築でつまずきがちな個人開発者には大きな助けになります。
はじめに一つ整理しておきます。2026年2月以降、Rork は Expo(React Native)ベースで iOS / Android 両対応アプリを生成する製品、Rork Max はネイティブ Swift アプリを生成する別製品という棲み分けになりました。Rork Max はクラウド Mac でコンパイルする独自の仕組みを持つため EAS を使いません。本記事で扱う EAS の対象は、Rork が出力する Expo(React Native)プロジェクトです。公式の製品ページだけでは混同しやすい部分なので、最初に押さえておいていただければと思います。
ここからは、私が6本のアプリ運用で実際に組んだ構成をベースに、セットアップから本番運用までを順に追っていきます。
EAS セットアップ — CLI の導入から eas.json まで
ステップ 1:EAS CLI のインストール
npm install -g eas-cli
バージョン確認:
eas --version
ステップ 2:Expo アカウントへのログイン
eas login
プロンプトに従ってブラウザで Expo アカウントにログインします。既存アカウントがない場合は、expo.dev から無料会員登録してください。
ℹ️ **ヒント**: Rork プロジェクトが複数ある場合、各プロジェクトで同じアカウントを使用するか、あるいは環境変数 `EXPO_TOKEN` に API トークンを設定して、CI/CD 環境での自動認証を可能にします。
ステップ 3:EAS ビルド設定の初期化
eas build:configure
このコマンドを実行すると、プロジェクトルートに eas.json ファイルが作成されます。以下は典型的な設定例です。
eas.json の詳細設定例
{
"cli" : {
"version" : ">= 5.0.0"
},
"build" : {
"development" : {
"distribution" : "internal" ,
"ios" : {
"buildType" : "development"
},
"android" : {
"buildType" : "development"
}
},
"preview" : {
"distribution" : "internal" ,
"ios" : {
"buildType" : "ad-hoc" ,
"provisioning" : {
"teamId" : "YOUR_APPLE_TEAM_ID"
}
},
"android" : {
"buildType" : "apk"
}
},
"production" : {
"distribution" : "store" ,
"ios" : {
"buildType" : "app-store" ,
"provisioning" : {
"teamId" : "YOUR_APPLE_TEAM_ID"
}
},
"android" : {
"buildType" : "aab"
}
}
},
"submit" : {
"production" : {
"ios" : {
"appleId" : "your-email@example.com" ,
"ascAppId" : "1234567890" ,
"appleTeamId" : "YOUR_APPLE_TEAM_ID"
},
"android" : {
"serviceAccount" : "path/to/service-account.json" ,
"track" : "production"
}
}
}
}
各プロファイルの説明 :
development : ローカル開発用。内部配布のみ
preview : QA チーム向けテストビルド。ストア申請なしで TestFlight(iOS)または Google Play Internal Testing(Android)経由で配布可能
production : App Store / Google Play へ本番リリースするビルド。AAB(Android App Bundle)と App Store 用 iOS ビルドを生成
EAS Build で iOS をビルドする
iOS ビルドの準備
iOS アプリを App Store にリリースするには、Apple Developer Account が必須です。アカウント取得後、以下の手順で進めます。
1. ビルド認証情報の設定
eas credentials
このコマンドでインタラクティブに以下を設定できます:
Apple ID とパスワード(またはアプリパスワード)
開発者証明書(Certificate)
プロビジョニングプロファイル
ℹ️ **セキュリティ上の注意**: Apple パスワードは絶対に GitHub や他の公開場所に保存しないこと。EAS が暗号化して安全に管理します。
2. 初めての iOS ビルド実行
eas build --platform ios --profile production
初回実行時、EAS はビルド ID をコンソールに表示し、ビルドキューに登録します。進捗を確認するには:
eas build --status
ビルド完了後、TestFlight 用の .ipa ファイルが生成されます。
3. TestFlight への配布
eas submit --platform ios --profile production
このコマンドで自動的に App Store Connect にアップロードされ、TestFlight で内部テスターが利用可能になります。
iOS ビルドのベストプラクティス
{
"build" : {
"production" : {
"ios" : {
"buildType" : "app-store" ,
"provisioning" : {
"teamId" : "YOUR_APPLE_TEAM_ID" ,
"signingCertificate" : "YOUR_CERT_ID"
},
"entitlements" : {
"com.apple.developer.pass-type-identifiers" : [
"pass.com.example.rorkmax"
]
}
}
}
}
}
EAS Build で Android をビルドして Google Play へ届ける
Android ビルド認証情報の設定
eas credentials
Android の場合、キーストア(Keystore)情報が必要です。初回実行時に自動生成することも、既存のキーストアをアップロードすることもできます。
キーストアの手動作成(既に持っている場合)
keytool -genkey -v -keystore my-release-key.keystore \
-keyalg RSA -keysize 2048 -validity 10000 \
-alias my-key-alias
生成されたキーストアを EAS に登録します。
Android APK / AAB ビルド
# APK ビルド(プレビュー用)
eas build --platform android --profile preview
# AAB ビルド(Google Play 本番用)
eas build --platform android --profile production
Android ビルド設定例
{
"build" : {
"production" : {
"android" : {
"buildType" : "aab" ,
"gradleCommand" : ":app:bundleRelease" ,
"withoutCredentials" : false ,
"env" : {
"TARGET_SDK_VERSION" : "34" ,
"MIN_SDK_VERSION" : "24"
}
}
},
"preview" : {
"android" : {
"buildType" : "apk" ,
"gradleCommand" : ":app:assembleRelease"
}
}
}
}
Google Play への配布
eas submit --platform android --profile production
Google Play Console の認証情報(サービスアカウント JSON ファイル)が必要です。以下の手順で準備します。
Google Cloud Console で新規プロジェクトを作成
Google Play Android Developer API を有効化
サービスアカウント キーを JSON 形式でダウンロード
eas.json の submit.production.android に serviceAccount パスを記載
{
"submit" : {
"production" : {
"android" : {
"serviceAccount" : "./path/to/service-account.json" ,
"track" : "production"
}
}
}
}
EAS Update — ストア審査を待たずに修正を届ける
OTA アップデートの可能範囲と制限
EAS Update は、ネイティブコード(Objective-C、Kotlin、Java)の変更を除く、JavaScript バンドルと静的アセット(画像、フォント)の更新を可能にします。
更新可能 :
React Native / Rork のビジネスロジック
UI コンポーネント
API 呼び出しロジック
CSS スタイル(StyleSheet)
更新不可 :
ネイティブモジュール追加
パーミッション変更
Expo SDK のメジャーバージョンアップ
OTA アップデートの実行
eas update --branch production --message "Critical bug fix: crash on user login"
このコマンドは以下の処理を実行します:
最新の JavaScript バンドルをビルド
EAS サーバーにアップロード
production ブランチの全デバイスに即座にロール会員ード
リリースノート付きアップデート
eas update --branch production \
--message "v1.2.1: Fixed dashboard load time" \
--metadata "{ \" environment \" : \" production \" , \" rollback_available \" : true}"
複数ブランチ戦略
# ステージング環境へのロールアウト
eas update --branch staging --message "Beta: new payment flow"
# 段階的ロールアウト(A/B テスト)
eas update --branch production \
--message "Variant A: new onboarding" \
--percent 25
--percent オプションで、ユーザーの何パーセントにアップデートを提供するかを制御できます。段階的ロールアウトにより、問題が見つかった場合の影響を最小化できます。
ロールバック戦略
# 最新リリースを表示
eas update --list
# 特定のリリースにロールバック
eas update --branch production --rollback-to-release-id=abc123def456
ℹ️ **本番運用のコツ**: OTA アップデートをリリース前に、必ずステージングブランチで検証してください。不具合が見つかった場合は、`--rollback-to-release-id` で即座に前のバージョンに戻せます。
GitHub Actions で CI/CD パイプラインを組む
ワークフロー YAML の実装
プロジェクトルートに .github/workflows/eas-ci.yml を作成します。
name : Rork EAS CI/CD
on :
push :
branches :
- main
- develop
pull_request :
branches :
- main
jobs :
test :
name : Run Tests
runs-on : ubuntu-latest
steps :
- name : Checkout code
uses : actions/checkout@v3
- name : Setup Node.js
uses : actions/setup-node@v3
with :
node-version : "18.x"
cache : "npm"
- name : Install dependencies
run : npm ci
- name : Run linter
run : npm run lint
- name : Run tests
run : npm run test -- --coverage
build-preview :
name : Build Preview (iOS & Android)
runs-on : ubuntu-latest
needs : test
if : github.event_name == 'push' && github.ref == 'refs/heads/develop'
steps :
- name : Checkout code
uses : actions/checkout@v3
- name : Setup Node.js
uses : actions/setup-node@v3
with :
node-version : "18.x"
cache : "npm"
- name : Setup EAS CLI
run : npm install -g eas-cli
- name : Install dependencies
run : npm ci
- name : Build iOS Preview
run : eas build --platform ios --profile preview --non-interactive
env :
EXPO_TOKEN : ${{ secrets.EXPO_TOKEN }}
- name : Build Android Preview
run : eas build --platform android --profile preview --non-interactive
env :
EXPO_TOKEN : ${{ secrets.EXPO_TOKEN }}
- name : Post build status
if : always()
uses : actions/github-script@v6
with :
script : |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `✅ Preview builds completed! Check EAS dashboard for download links.`
})
build-production :
name : Build Production & Deploy OTA
runs-on : ubuntu-latest
needs : test
if : github.event_name == 'push' && github.ref == 'refs/heads/main'
steps :
- name : Checkout code
uses : actions/checkout@v3
- name : Setup Node.js
uses : actions/setup-node@v3
with :
node-version : "18.x"
cache : "npm"
- name : Setup EAS CLI
run : npm install -g eas-cli
- name : Install dependencies
run : npm ci
- name : Build iOS Production
run : eas build --platform ios --profile production --non-interactive
env :
EXPO_TOKEN : ${{ secrets.EXPO_TOKEN }}
- name : Build Android Production
run : eas build --platform android --profile production --non-interactive
env :
EXPO_TOKEN : ${{ secrets.EXPO_TOKEN }}
- name : Deploy OTA Update
run : |
eas update \
--branch production \
--message "Release: $(git describe --tags --abbrev=0 2>/dev/null || echo 'v0.0.1')" \
--non-interactive
env :
EXPO_TOKEN : ${{ secrets.EXPO_TOKEN }}
- name : Notify Slack
if : always()
uses : slackapi/slack-github-action@v1.24.0
with :
webhook-url : ${{ secrets.SLACK_WEBHOOK }}
payload : |
{
"text": "🚀 Rork Production Deployment",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Status*: ${{ job.status }}\n*Branch*: main\n*Commit*: <${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }}|${{ github.sha }}>"
}
}
]
}
GitHub Secrets の設定
リポジトリの Settings > Secrets and variables > Actions で以下を登録します:
ℹ️ **CI/CD 本番運用**: `--non-interactive` フラグを使用すると、ユーザー入力を待たずにコマンドが実行されます。GitHub Actions 環境での必須オプションです。
環境変数とシークレットをどう安全に扱うか
EAS Secret の作成
eas secret:create
プロンプトに従って以下の情報を入力します:
シークレット名(例:API_KEY_PRODUCTION)
シークレット値
作成されたシークレットは EAS クラウドで暗号化されます。
app.json での環境変数定義
{
"expo" : {
"name" : "Rork App" ,
"slug" : "rorkmax-app" ,
"version" : "1.0.0" ,
"plugins" : [
[
"expo-build-properties" ,
{
"ios" : {
"deploymentTarget" : "14.0"
},
"android" : {
"targetSdkVersion" : 34
}
}
]
]
}
}
React Native コード内での環境変数使用
import Constants from 'expo-constants' ;
export const config = {
apiBaseUrl: Constants.manifest?.extra?. API_BASE_URL || 'https://api.example.com' ,
environment: Constants.manifest?.extra?. ENVIRONMENT || 'development' ,
analyticsToken: Constants.manifest?.extra?. ANALYTICS_TOKEN || '' ,
};
// API 呼び出し
const response = await fetch ( `${ config . apiBaseUrl }/users` , {
headers: {
'Authorization' : `Bearer ${ config . analyticsToken }` ,
},
});
プロファイル別環境変数の設定
eas.json で各プロファイルに異なる環境変数を指定:
{
"build" : {
"development" : {
"env" : {
"API_BASE_URL" : "http://localhost:3000" ,
"ENVIRONMENT" : "development" ,
"DEBUG" : "true"
}
},
"preview" : {
"env" : {
"API_BASE_URL" : "https://staging-api.example.com" ,
"ENVIRONMENT" : "staging"
}
},
"production" : {
"env" : {
"API_BASE_URL" : "https://api.example.com" ,
"ENVIRONMENT" : "production"
}
}
}
}
GitHub Actions 環境で環境変数を利用する場合:
- name : Deploy to Production
env :
API_BASE_URL : ${{ secrets.PRODUCTION_API_URL }}
ANALYTICS_TOKEN : ${{ secrets.ANALYTICS_TOKEN }}
run : eas update --branch production --non-interactive
プレビュービルドで QA を回す
プレビュービルドの作成と配布
eas build --platform ios --profile preview --non-interactive
プレビュービルド完成後、EAS が QR コードを生成します。iOS デバイスから Expo Go アプリで QR コードをスキャンすると即座にテスト可能。
複数チームメンバーへの配布
eas build --platform ios --profile preview --non-interactive \
--message "QA Review: Payment flow v2" \
--auto-submit
--auto-submit を指定すると、ビルド完了時に自動的に TestFlight(iOS)に登録され、登録済みテスターに通知されます。
プレビュー環境での環境変数
{
"build" : {
"preview" : {
"env" : {
"API_BASE_URL" : "https://staging-api.example.com" ,
"ENABLE_DEV_MENU" : "true" ,
"LOG_LEVEL" : "debug"
}
}
}
}
ℹ️ **QA チーム向けのヒント**: プレビュービルドには本番環境とは異なるサーバー(ステージング)を指すよう設定し、デバッグメニューを有効化します。これにより、デバイスで直接ネットワークリクエストやロジックを検証できます。
料金プランと制限 — 無料枠でどこまで運用できるか
EAS Free プラン
ビルド枠 : 月30ビルドまで(私の6本運用では、本番ビルドだけで 6本 × 2 プラットフォーム = 12 を消費します)
同時ビルド : 1 個(キューの優先度は低めで、混雑時間帯は待ちが発生します)
EAS Update : 小規模な配信であれば無料枠内で利用できます
EAS Production プラン($99/月〜)
ビルドクレジット : 月額に相当するビルドクレジットが含まれ、超過分は従量課金
同時ビルド : 複数並列が可能
キュー優先度 : 高め(混雑時間帯でも着手が早い)
本番環境で週次以上のリリースを行う場合は、有料プランの導入が現実的です。なお料金体系は改定されることがあるため、最新の条件は公式の料金ページでご確認いただくのが確実です。
ビルド時間の削減のコツ
# キャッシュを活用
eas build --platform ios --profile production
# 前回のビルドログを確認
eas build --status --detailed
ℹ️ **コスト最適化**: 開発段階ではプレビュービルドを多用し、本番リリース時のみ本格ビルドを行うことで、月々のビルド時間を圧縮できます。
本番運用チェックリスト
Rork アプリを EAS で本番環境にリリースする際の確認事項:
ビルド前の確認
[ ] eas.json で production プロファイルが正しく設定されているか
[ ] Apple Developer Team ID および Google Play サービスアカウント JSON が登録済みか
[ ] 全環境変数(API_KEY、ANALYTICS_TOKEN など)が EAS Secret に登録済みか
[ ] iOS の Provisioning Profile と Certificate が有効期限内か
テスト段階
[ ] プレビュービルドを iOS/Android で実行し、動作確認
[ ] ステージング環境の API に接続してエンドツーエンドテスト
[ ] ネットワーク遅延・オフライン状態での挙動をテスト
[ ] 複数バージョンの iOS/Android デバイスで検証
リリース実行
[ ] Git リポジトリに最新コードが反映されているか確認
[ ] npm run lint / npm run test で全テストをパス
[ ] バージョン番号を package.json / app.json で更新
[ ] リリースノートを準備
リリース後の監視
[ ] Crashlytics でクラッシュレートを監視
[ ] Analytics でセッション数・アクティブユーザー数を追跡
[ ] ユーザーレビュー・サポート問い合わせを監視
[ ] 問題が見つかった場合は速やかに OTA ロールバック
個人開発の現場で運用して見えてきたこと
導入から数か月、6本のアプリのリリースを EAS 経由に切り替えて感じたことを、ありのままに書き留めておきます。
まず効いたのは OTA アップデートです。ある壁紙アプリで、一覧画面のページネーションに重複表示の不具合が見つかったとき、JavaScript のみの修正だったため eas update でその日のうちに配信できました。従来であれば審査提出から反映まで1〜2日待つところです。不具合を見つけたその日に直せるという安心感は、レビュー欄の星にも静かに表れてきます。
一方で、無料プランのビルド枠は6本運用では確実に足りなくなります。月30ビルドの枠は、6本 × iOS / Android の本番ビルドだけで12を消費し、プレビュービルドを含めるとすぐ上限に達しました。私の場合は、本番ビルドを EAS に、開発中の動作確認はローカルの開発ビルドと Expo Go に寄せることで、無料枠内に収める月と、リリースが重なる月だけ課金する運用に落ち着いています。毎週リリースするチームなら有料プラン($99/月〜)の価値は十分ありますが、月1回ペースの個人開発であれば無料枠でも工夫次第で回る、というのが私の実感です。
ビルドの待ち時間も書き添えておきます。無料プランはキューの優先度が低く、混雑する時間帯(米国の日中、日本時間では深夜〜早朝)には着手まで20〜40分待つことがありました。私は日本時間の午前中にビルドをまとめて投げる運用にしてから、この待ち時間のストレスがほとんどなくなりました。
次の一歩
まずは1本、既存の Rork(Expo)プロジェクトで eas build:configure を実行し、プレビュービルドをご自身の端末にインストールするところまで試してみてください。クラウドでビルドが完了し、QR コードから実機に入る瞬間に、この仕組みが日々のリリース作業をどう変えるかが体感できるはずです。GitHub Actions 連携は、手動の流れに慣れてからで十分です。
リリース作業に追われている方の参考になれば幸いです。お読みいただきありがとうございました。
参考リソース