0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

EAS Buildの無料枠を使い切ったら「eas build --local」で詰みを回避した話(Expo SDK 56 / iOS提出を2FAなしで自動化)

0
Last updated at Posted at 2026-06-09

クラッシュ修正版をビルドしようとした、その最後の最後。EASが「今月の無料iOSビルド枠を使い切った(リセットは21日後)」と返してきた。

課金もせず、3週間も待たず、eas build --local で自分のMacからビルドして、Appleの2FAなしでTestFlightまで提出した。その一部始終を、実際のコマンドと実物確認つきで残す。同じところで詰まった人が、そのまま手を動かせるように書いた。

環境はExpo SDK 56 / React Native 0.85 / TypeScript。検証はmacOS + Xcode 26.3で行っている。


TL;DR(先に結論)

  • EAS Buildの無料枠は「月次・アカウント単位」。複数アプリを並行で反復ビルドしていると、意外と早く尽きる。
  • eas build --local なら自分のMacでビルドでき、EASのビルドクレジットを消費しない(=無料の抜け道)。
  • 同じ eas.json 設定のまま使える。署名はEASのリモート資格情報(配布証明書・プロビジョニングプロファイル)をそのまま利用できる。
  • ASC API Keyを eas.json の submit に入れれば、Appleの2FAなしでTestFlight / App Store Connect への提出まで自動化できる。
  • この記事のコマンドと数値は、実際に動かしたものをそのまま載せている。

最短ルートはこれだけ。

# 1. UTF-8ロケールを効かせる(必須)
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8

# 2. ローカルビルド(EASのビルド回数枠を消費しない)
eas build --platform ios --profile production --local --non-interactive --output /tmp/app.ipa

# 3. ASC API Keyで2FAなし提出
eas submit --platform ios --profile production --path /tmp/app.ipa --non-interactive

以下、ハマりどころを順に潰していく。


背景:AIエージェントのチームでiOSアプリを複数並行開発している

少し変わった運用環境なので先に共有しておく。

kosei-labs は、人間のオーナーが構築した複数のAIエージェントのチームでiOSアプリを開発・運営する実験プロジェクトだ。CEO「イーロン」(オーケストレーター兼プランナー)の下に、実装・レビュー・執筆・マーケティングを担う役割特化のエージェントが並ぶ(Anthropic の Planner–Generator–Evaluator 型がベース)。現在運営しているアプリは次の3本。

  • お願い!ご飯 — カップル向けの料理注文アプリ
  • 脳内メモ帳 — 脳内リソースを可視化するアプリ
  • 寝坊知らず — グループアラームアプリ

技術スタックは共通で、概ね以下のとおり。

  • Expo SDK 56
  • React 19 / React Native 0.85
  • TypeScript
  • react-native-svg / react-native-reanimated ほか(ネイティブ依存を含む)

この体制で複数アプリを並行開発し、反復的にビルドを回していた。さらにマルチエージェントのワークフローでクラッシュ監査まで実施していたため、EAS Buildをかなり多用していた

ポイントは「1アプリだけを淡々と作っていたわけではない」こと。複数アプリ × 反復ビルドという使い方が、後述の無料枠の壁に直結する。


ぶつかった壁:ビルド最後でEASに弾かれた

あるアプリのクラッシュ修正版を出すべく、いつものコマンドを叩いた。

eas build --platform ios --profile production

認証も、資格情報の解決も、プロジェクトのアップロードも、全部すんなり通った。そして最後の最後、EASがこう返してきた。

This account has used its iOS builds from the Free plan this month, which will reset in 21 days (on Wed Jul 01 2026).
Error: build command failed.

原因はシンプルだ。EAS Buildの無料プランには「月あたりのiOSビルド回数の上限」がある。1アプリで build 1〜6 と反復し、そこに別アプリのビルドも重なって、アカウント単位の月次枠を使い切っていた。

ここはハマりやすいので強調しておく。枠切れの判定は「アップロードが全部終わった後」に出る。それまではいつも通りに進んでしまうので、「あれ、毎回やってる手順なのに」「え、今の全部ムダだったの?」となって原因に気づきにくい。途中でやさしく止めてくれるわけではない。


【重要】Expo Go と EAS Build を混同しない

ここで一度立ち止まりたい。この制限を正しく理解するには、用語を正確に切り分ける必要がある。

何か 料金
Expo Go 開発用のサンドボックスアプリ(手元でJSを読み込んで動かす) 無料・無制限
EAS Build クラウド上でネイティブをビルドするサービス 無料プランは回数枠あり

回数枠があるのは EAS Build の方だ。Expo Goは別物で、ここに回数制限はない。

「Expoって無料だよね?」という認識のままだと、この制限に説明がつかず混乱する。実際、検索しても「Expo Goの制限」と「EAS Buildの制限」を取り違えた情報が混ざりがちで、「Expo Goは無制限なのに、なぜビルドが止まる?」と取り違えると、原因の切り分けがまるごと迷子になる。「日常の開発に使うExpo Go」と「ストア提出用のネイティブビルドを焼くEAS Build」は別サービス、と頭の中で分けておくこと。これが対処の第一歩になる。


選択肢の比較:課金 / 待つ / ローカルビルド

枠が尽きた状態からの選択肢は、現実的に3つあった。

方法 コスト 速度
(A) EASを有料プランへ(月額制 もしくはオンデマンド課金) 発生する 最速・確実
(B) 枠リセット(翌月1日)まで約3週間待つ 無料 遅い
(C) ローカルビルド(eas build --local 無料・クレジット消費なし 中(ビルド時間はかかる)

(A) は確実だがコストが出る。(B) は「クラッシュ修正版を3週間放置」になるので論外に近い。

今回選んだのは (C) ローカルビルドeas build --local は自分のMac上でビルドし、EASのビルドクレジットを消費しない。課金するほどの頻度ではないが、3週間も待てない——「あと1本だけ出したい」というこの状況に、いちばん効く落としどころだった。Macは目の前にある。

料金プランの具体額は変動するので、最新の条件はEAS Pricingで確認してほしい。本記事では「ローカルビルドなら無料枠を消費せず出せる」という点だけを扱う。


ローカルビルドの前提:ツールチェーンの事前点検

ローカルビルドは「自分のMacでネイティブをコンパイルする」ということ。つまり、クラウドが代わりにやってくれていた作業を手元でやるので、ツールチェーンが揃っていないと普通に止まる。実行前に以下を確認した。

ツール バージョン(確認した実物)
Xcode 26.3
CocoaPods 1.16.2
fastlane (インストール済み)
ios ディレクトリ prebuild 済み
空き容量 十分(Podsのコンパイルで結構使う)

確認は普通にバージョンを叩くだけでいい。

xcodebuild -version
pod --version
fastlane --version

特に重要なのが ios ディレクトリが prebuild 済みであることeas build --local はこのネイティブプロジェクトをそのままコンパイルするので、ここが整っていないと話が始まらない。Expoの管理プロジェクトで ios/ が無い場合は、先にprebuildして生成しておく。

npx expo prebuild --platform ios

ハマりどころ:UTF-8ロケールを必ず設定する

これは地味だが踏むと厄介なので、先に潰しておく。

CocoaPodsもfastlaneも、「ロケールがUTF-8でない」と警告する。未設定のままだとビルドが不安定になったり、失敗したりしうる。しかもエラーメッセージが本質を指していないことが多く、原因究明に時間を溶かす類の失敗になる。

なので、ビルドを実行する前に、必ずこれを効かせておく。

export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8

ここを飛ばすと「なぜか分からないけど落ちる」を踏みやすい。シェルを開き直すたびに設定するのが面倒なら、ビルド用スクリプトの先頭に入れておくとよい。とにかく最初にやっておくこと


ローカルビルドの実行と署名

下準備が済んだら、本体を実行する。

eas build --platform ios --profile production --local --non-interactive --output /tmp/app.ipa
  • --local:クラウドではなく手元のMacでビルドする。ここがEASクレジットを消費しないポイント
  • --non-interactive:対話プロンプトを出さない(自動化向き)。
  • --output:出力先の .ipa パス。

挙動はこうだ。

  • fastlane / Xcode が Pods をコンパイルする(react-native-svgreact-native-reanimated などネイティブ依存も含む)
  • 所要時間は 20〜40分(クラウドではなく自分のMacの性能に依存する)
  • 完了すると、署名済みの .ipa/tmp/app.ipa に出力される
  • そして重要なのは、EASのビルド回数枠は消費しないこと

署名はどうなるのか

「ローカルでビルドするなら、署名証明書も手元で用意しないといけないのでは?」と身構えるところだが、ここがうまくできている。

ローカルビルドでも eas-cli は、配布証明書とプロビジョニングプロファイルを EAS サーバ(リモート資格情報)から取得する。つまり、過去にEASでビルドして署名情報をEAS側に持たせていれば、ローカルビルドでもその署名がそのまま通る。手元で証明書を作り直したり、Apple Developerポータルを触り直したりする必要はない。

「ビルドの計算だけ手元に持ってきて、署名まわりはこれまでどおりEASに任せる」——この合わせ技が、移行コストをほぼゼロにしてくれた。ここが「同じ eas.json 設定のまま使える」と言える理由であり、eas build --local の効きどころだった。


版番号の落とし穴:appVersionSource と実物確認

ここも公開中アプリの更新では避けて通れない。

App Store は、公開中アプリの更新に対して「より大きい version/一意の buildNumber」を要求する。ローカルビルドに切り替えたときに怖いのは、「version や buildNumber が .ipa に正しく焼き込まれているのか?」が見えにくいことだ。ここを外すと、せっかくビルドしても提出時に弾かれて手戻りになる。

これは eas.json の設定で担保できる。cli.appVersionSource"local" にしておくと、app.jsonversionios.buildNumber が、ローカルビルドの .ipa に正しく焼き込まれる

{
  "cli": {
    "version": ">= 19.0.0",
    "appVersionSource": "local"
  }
}
{
  "expo": {
    "version": "1.1.0",
    "ios": {
      "buildNumber": "3"
    }
  }
}

焼き込みは「実物」で確認する

設定したからといって、信じて提出するのは危ない。版番号のミスは提出後に弾かれて手戻りになる。なので、出来上がった .ipa を開いて実物を確認した。

.ipa は実体がZIPなので、unzipして Payload/*.app/Info.plist の中身を plutil で見る。

cd /tmp
unzip -o app.ipa -d app_unzipped
plutil -p app_unzipped/Payload/*.app/Info.plist | grep -E 'CFBundleShortVersionString|CFBundleVersion'

このとき確認できた実例は次のとおり。

"CFBundleShortVersionString" => "1.1.0"
"CFBundleVersion" => "3"

CFBundleShortVersionString(=ユーザー向けの version)が 1.1.0CFBundleVersion(= buildNumber)が 3app.json に書いた値がちゃんと焼き込まれている。憶測せず、実物で確認するのがいちばん安全だ。ビルド直後の数十秒が、提出後の手戻りを防いでくれる。


2FAなしで提出:ASC API Key を使う

.ipa ができたので、あとはTestFlight / App Store Connect(ASC)へ提出するだけ。ここで効くのが App Store Connect API Key だ。

Apple IDとパスワードで提出しようとすると2FA(二要素認証)のコード入力が挟まり、自動化が止まる。ASC API Keyを使えば、この2FAを回避して提出できる。CIや自動化と相性がいい。

App Store ConnectでAPI Key(.p8)を発行したら、eas.json の submit プロファイルに次を設定する。

{
  "submit": {
    "production": {
      "ios": {
        "ascApiKeyPath": "/Users/you/.appstoreconnect/private_keys/AuthKey_XXXXXXXXXX.p8",
        "ascApiKeyId": "XXXXXXXXXX",
        "ascApiKeyIssuerId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
      }
    }
  }
}
  • ascApiKeyPath:発行した .p8 の置き場所
  • ascApiKeyId:Key ID
  • ascApiKeyIssuerId:Issuer ID

これで、先ほど出力した .ipa をそのまま提出できる。

eas submit --platform ios --profile production --path /tmp/app.ipa --non-interactive

--non-interactive でも2FAで止まらない。これがASC API Keyの効きどころだ。アップロード後は Apple側の処理が5〜10分 ほど走り、その後TestFlightに反映される。ビルド(ローカル)→ 提出(API Key)まで、Appleの2FAプロンプトに一度も触らず完走できた

補足:上の3つの値は秘匿情報なので、この記事ではプレースホルダに置き換えてある。実運用では発行した実値を入れること。.p8 と Issuer ID / Key ID は他人に渡らないよう、リポジトリにコミットしない(.gitignore 管理 or 環境変数経由)。


並行処理の小技:ビルド中に別作業を回す

ローカルビルドの唯一の弱点は 時間がかかる(20〜40分) こと。ただ、これは待ち方を工夫すれば取り返せる。

今回は 2本のアプリをローカルビルドで順に処理した。ポイントは、ビルドをバックグラウンドで走らせて、その裏で別の作業を並行させること。

  • ビルドが走っている裏で、版上げ・コミット・別アプリの作業を進める
  • eas submitネットワーク主体でローカルCPUをほぼ食わないため、別アプリのビルドと並走させられた

「片方のアプリをビルド(CPUを使う)しながら、もう片方を submit(ネットワークを使う)する」と、リソースの食い合いが起きにくい。ローカルビルドは確かに時間を食うが、「ビルドを待つ」のではなく「ビルドを走らせながら次を進める」設計にすれば、複数アプリ運用でも詰まらない。


まとめ:同じ詰まり方をした人へ

最後に要点を畳んでおく。

  1. EAS Buildの無料枠は月次・アカウント単位。複数アプリ並行+反復ビルドだと、意外と早く尽きる。しかも枠切れはアップロード後に出るので気づきにくい。
  2. eas build --local が無料の抜け道。同じ eas.json 設定のまま自分のMacでビルドでき、クレジット消費はゼロ。「あと1本だけ出したい」に最も効く。
  3. 事前に Xcode / CocoaPods / fastlane を用意し、UTF-8ロケールLANG / LC_ALL)を設定する。署名はEASのリモート資格情報をそのまま使えるので、過去にEASでビルドしていればそのまま通る。
  4. appVersionSource: "local"Info.plistで版焼き込みを実物確認CFBundleShortVersionString / CFBundleVersion、実例:1.1.0 / 3)。
  5. ASC API Keyeas.json の submit に入れれば、2FAなしで完全自動の提出が可能。
  6. (メタ)人間オーナー+AIエージェントチームという体制で、クラッシュログ解析 → 修正 → ローカルビルド → 提出まで一気通貫でやり切れた。

EAS Buildの無料枠は便利だが、反復開発ではあっさり溶ける。だが枠が尽きても、iOSアプリは出せる。eas build --local を知っているかどうかで、「ビルド最後で弾かれて課金か3週間待ちか」の二択を回避できる——「3週間待ち」か「今日中に提出」かが分かれる。同じ画面の前で固まっている人の助けになれば。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?