TL;DR
- 問題: Expo の EAS Build 無料枠(iOS 月15ビルド)をリリース直前に使い切ってしまい、App Store に提出できなくなった
-
解決: GitHub Actions の macOS ランナーで
expo prebuild→xcodebuild archiveを実行して IPA を生成し、アップロードはeas submitに任せた - コスト: 無料(Expo 有料プラン $19/月 の回避に成功)
- 対象読者: Mac を持たずに Expo で iOS アプリを開発している個人開発者、EAS Build 枠に悩む人、がんがん個人開発したい人
AI駆動開発でスピードアップしても、App Storeへ申請を待たないといけないなら意味ないですよね💦
前提条件
この記事の解決策は、以下の開発環境・制約の下で実施したものです。似た状況の方の参考になれば幸いです。
開発環境
- OS: WSL2 (Ubuntu 22.04) — Mac は所有していない
- Node.js: v20
- Package manager: npm
- Git ホスティング: GitHub (Private リポジトリ)
-
CLI ツール:
gh(GitHub CLI),eas-cli - エディタ: VS Code + Claude Code
アプリ構成
- Expo SDK: 52(React Native 0.76 / New Architecture 有効)
- 言語: TypeScript
- フレームワーク: Expo managed workflow + expo-router
- 主要ライブラリ: Clerk(認証), RevenueCat(IAP), expo-notifications(プッシュ通知)
- ターゲット: iOS のみ(Android は後回し)
Apple 側の準備(前提)
- Apple Developer Program に登録済み(個人アカウント)
- App Store Connect にアプリ登録済み
- Distribution Certificate(.p12)と Provisioning Profile(.mobileprovision)は EAS で発行済み
- App Store Connect API Key(.p8)も発行済み
Expo 側の準備(前提)
- Expo アカウント登録済み(https://expo.dev — 無料プランで OK)
-
eas-cliインストール済み:npm install -g eas-cli -
eas loginで認証済み - プロジェクトが
eas init済み(eas.jsonが存在する) -
eas credentialsで iOS の証明書・プロファイル発行済み
この解決策でも Expo 無料アカウントは必要 です。ただし 有料プラン($19/月)は不要。使うのは eas credentials(証明書管理)と eas submit(アップロード)のみで、これらはビルド枠とは別料金体系で無料です。詳しくは後述のコラムで説明します。
制約
この解決策を必要とした「制約」は以下の3点です:
-
Mac を持っていない
- ローカルで
xcodebuildを直接実行できない - これまでビルドは全て EAS Build のクラウド Mac に頼っていた
- ローカルで
-
EAS Build 無料枠の枯渇
- 無料プランでは iOS 月15ビルドまで(※本記事執筆時点)
- リリース前の詰めの段階で試行錯誤を重ねた結果、月末を待たずに枯渇
- リリース直前だったので「来月まで待つ」の選択肢が事実上なかった(というか待ってられない)
-
有料プランにコストをかけたくない
- Expo Production プラン: $19/月(年額 $228)
- 個人開発の小規模アプリでは正直重い
- 一時的な枠切れのために継続課金するのは避けたい
何が起きたか
リリース直前、eas build を実行した際に以下のエラーが出ました。
Compressing project files and uploading to EAS Build. Learn more:
https://expo.fyi/eas-build-archive
✔ Uploaded to EAS 11s
✔ Computed project fingerprint
This account has used its iOS builds from the Free plan this month,
which will reset in 20 days (on Fri May 01 2026).
Upgrade your plan for more builds with shorter wait times...
Error: build command failed.
「あと20日待て、または課金しろ」という状況。リリース日が迫っていたので、両方拒否したい…という状態で代替策を模索。
重要な洞察: EAS Build と EAS Submit は別物
解決策の核心はここにあります。
| サービス | 役割 | 無料枠制限 |
|---|---|---|
| EAS Build | クラウド Mac で React Native プロジェクトをビルド → IPA を生成 | 月15回(iOS) |
| EAS Submit | 手元の IPA を App Store Connect にアップロード | 制限なし |
つまり、ビルドだけ別の手段で行えば、アップロードは引き続き EAS を使えるわけです。EAS Submit は Apple ID と API Key の管理をよしなにやってくれるので、自前で altool や Transporter を使うより圧倒的に楽です。
図解: 通常フロー vs 本記事のハイブリッドフロー
ビルド工程だけが差分で、アップロード工程(EAS Submit)は両方のパスで全く同じものを使うのがポイントです。図では EAS Submit を共有ノードにして、それを視覚化しています。
見ての通り、差分は Build 工程(左側)だけで、その先の EAS Submit → App Store Connect は完全に同一の経路です。この記事の核心洞察はここにあります: ビルドだけ差し替えれば、面倒なアップロード工程は EAS にそのまま任せられる。Apple ID 認証や API Key の扱いで消耗せずに済みます。
🤔 コラム: EAS Build を使わないのに、なぜ Expo アカウントが必要?
「GitHub Actions でビルドするなら Expo 無しで完結できるのでは?」と思うかもしれませんが、この記事の構成では Expo 無料アカウントは必須 です。理由は3つ。
1. 証明書管理を eas credentials に委譲している
Apple Developer Portal で Distribution Certificate や Provisioning Profile を自前で発行・管理するのは相当面倒です。CSR 生成、秘密鍵の管理、Provisioning Profile の更新、デバイス登録…… これを eas credentials が全て自動でやってくれます。
npx eas credentials
# → iOS → production → Set up everything
# → あとはメニュー選ぶだけで証明書・プロファイルが揃う
2. アップロードを eas submit に任せている
xcrun altool や Transporter を自前で扱うと、ASC API Key の認証エラーで消耗します(筆者は CryptoKitError -7 で半日溶かしました)。eas submit は同じ認証情報で一発で通ります。
3. GitHub Actions 内で eas-cli を使うセットアップに EXPO_TOKEN が要る
ワークフロー内の expo/expo-github-action@v8 が eas-cli をインストール&認証するために EXPO_TOKEN を参照します。
使う EAS 機能と課金の関係
| EAS 機能 | 使うか? | 制限 | 無料プランで使える? |
|---|---|---|---|
eas credentials |
✅ 使う | なし | ✅ 無料で OK |
eas build |
❌ 使わない(GitHub Actions で代替) | 月15回 | — |
eas submit |
✅ 使う | なし | ✅ 無料で OK |
つまりこのハイブリッド構成は 「EAS の重量級ワーカー(Build)だけ迂回して、軽量な周辺機能(credentials + submit)は活用する」 という設計です。Expo Production プラン($19/月)への課金は一切不要です。
EXPO_TOKEN の取得方法
GitHub Actions 内で EAS CLI を認証させるには Expo のアクセストークン が必要です。取得手順は以下の通り。
- https://expo.dev にログイン
- 右上のアバター → Account settings → Access tokens
- 「Create token」をクリック
- トークン名を入力(例:
github-actions-ios-build)→ Create - 表示されたトークン文字列をコピー(一度しか表示されないので注意)
- GitHub リポジトリの Secrets に登録:
gh secret set EXPO_TOKEN --body "<コピーしたトークン>"
このトークンは EAS CLI の認証だけに使われ、Apple や App Store Connect へのアクセスには関与しません。漏れても App Store に直接影響はないものの、Expo プロジェクトへのアクセス権を持つので厳重に管理してください。
EXPO_TOKEN の使われ方
ワークフロー YAML の以下の部分で使われます:
- name: Setup Expo
uses: expo/expo-github-action@v8
with:
eas-version: latest
token: ${{ secrets.EXPO_TOKEN }} # ← ここで認証
この expo/expo-github-action@v8 アクションが:
- Node 環境に
eas-cliをインストール -
EXPO_TOKENで非対話的にログイン - 以降のステップで
npx expo prebuild等が認証済み状態で動く
ようにしてくれます。対話的な eas login を CI で走らせずに済むのでこの方法が必須です。
解決策: GitHub Actions の macOS ランナーでビルド
なぜ GitHub Actions か
-
macOS ランナーが使える:
macos-26(Xcode 26.2) などが標準で提供されている - 無料枠が別腹: GitHub Actions は Private リポジトリで月2000分の無料枠。ただし macOS ランナーは10倍の係数で消費されるので実質200分
- 1ビルド ≒ 25-30分 → 月6〜7回くらいは無料で回せる
- Public リポジトリなら実質無制限
- 使い慣れている: CI/CD として既に馴染みがある
もし GitHub Actions 無料枠も足りなくなったら、リポジトリを Public に変更すれば無制限になります。機密情報は GitHub Secrets で管理されているので安全です。
全体フロー
ローカル(WSL)、GitHub Actions の macOS ランナー、App Store Connect の3つのアクターがどう連携するかをシーケンス図で示します。
この図のポイントは、開発者はローカルからコマンドを叩くだけで、重い仕事はすべてクラウド(GitHub Actions + App Store Connect)でやってくれる点です。Mac を持っていなくても iOS リリースが完結します。
📘 コラム: そもそも .p12 って何?
次のセクションでいきなり base64 -w0 credentials/ios/dist-cert.p12 のようなコマンドが出てきますが、「そもそも .p12 って何?」という方のために軽く整理しておきます。iOS の署名まわりは独特の用語が多くて最初つまづきやすいポイントなので、ここで押さえておくと後が楽になります。
.p12 = PKCS#12 形式の証明書ファイル
中身は2つセットで入っています:
-
証明書本体(Apple が発行した公開鍵)
- 「このアプリは本物の ◯◯ が署名したものだ」と証明する電子的ハンコ
-
秘密鍵(Private Key)
- 実際にアプリに署名する時に使う鍵
iOS アプリに署名するには 証明書 + 秘密鍵の両方 が必須です。.cer(証明書のみ)形式のファイルでは署名できません。
拡張子の違い早見表
| 拡張子 | 中身 | 主な用途 |
|---|---|---|
.cer |
証明書のみ(公開鍵) | Apple Developer Portal からダウンロードされるファイル |
.p12 |
証明書 + 秘密鍵 | Keychain Access からエクスポート、CI/CD で使う |
.mobileprovision |
Provisioning Profile | 「どの証明書・どの Bundle ID・どのデバイスで使えるか」の紐付け定義 |
.p8 |
App Store Connect API Key | App Store Connect への API アクセス用(アップロード認証など) |
【補足】.p8 とは?(.p12 と別物です)
後ほど APP_STORE_CONNECT_API_KEY_BASE64 という Secret も登録しますが、ここで使うのは .p12 ではなく .p8 という別フォーマットのファイルです。名前が似ているので混乱しやすいポイント。
- .p12 = アプリへの署名に使う(Distribution Certificate + 秘密鍵)
- .p8 = App Store Connect API への認証に使う(Apple が発行する ECDSA 秘密鍵、PEM 形式)
.p8 は App Store Connect の「Users and Access」→「Integrations」→「App Store Connect API」でキーを発行するとダウンロードできます。ファイル名は AuthKey_XXXXXXXXXX.p8 のようになっていて、この XXXXXXXXXX が Key ID です(APP_STORE_CONNECT_API_KEY_ID Secret として登録する値)。
.p8 は一度しかダウンロードできません。失くしたら再発行が必要なので、ダウンロード直後に安全な場所にバックアップしましょう。
役割の違いを整理するとこうなります:
| ファイル | 誰が・いつ使うか |
|---|---|
.p12 |
xcodebuild が IPA を作る時にアプリに署名する |
.p8 |
eas submit や altool が IPA を App Store Connect にアップロードする時に API 認証する |
つまり 「ビルド時の署名」と「アップロード時の認証」は別の鍵で行われている、というのが iOS リリースの仕組みです。
パスワードの役割
.p12 は書き出すときに必ずパスワードを設定します。これが GitHub Secrets の DISTRIBUTION_CERT_PASSWORD の正体です。
.p12 ファイル単体が漏れてもパスワードが分からなければ秘密鍵は取り出せません。とはいえ両方漏らすと完全にアウトなので、.p12 は絶対にコミットしないこと。
CI/CD での流れ(.p12 と .p8 の両方)
.p12(署名用)と .p8(API認証用)が、それぞれ別のタイミングで使われる様子を図示します。
この図で見えてくる重要な事実:
- .p12 は署名工程(xcodebuild) で使われる
- .p8 はアップロード工程(eas submit) で使われる
- 2つの鍵は別のタイミング・別の目的で使われる → 混同しないよう注意
つまり次のセクションでやる作業は、「ローカルで持っている .p12 / .p8 を GitHub Actions のクラウド Mac に安全に渡して、それぞれ適切なタイミングで使わせる」 ということです。これが分かっていれば、後のコマンドの意味がスッと頭に入ります。
事前準備: GitHub Secrets の登録
まずビルドに必要な認証情報を GitHub Secrets に登録します。
Secrets の全体マップ
登録する Secret は合計で12個ほどあります。最初に全体像を把握すると混乱しません。役割ごとに4グループに分けると整理しやすいです。
この分類を頭に入れておくと、後で「あれ、この Secret どこで使うんだっけ?」と迷わなくなります。
必要な Secrets 一覧
| Secret 名 | 内容 | 取得元 |
|---|---|---|
EXPO_TOKEN |
Expo アクセストークン(取得手順は先ほどのコラム参照) | https://expo.dev → Account settings → Access tokens |
APPLE_TEAM_ID |
Apple Developer Team ID(例: ABCDE12345) |
Apple Developer アカウント |
DISTRIBUTION_CERT_BASE64 |
Distribution Certificate (.p12) の Base64 | eas credentials |
DISTRIBUTION_CERT_PASSWORD |
.p12 のパスワード | eas credentials |
PROVISIONING_PROFILE_BASE64 |
Provisioning Profile の Base64 | eas credentials |
PROVISIONING_PROFILE_NAME |
Provisioning Profile の名前 | eas credentials |
APP_STORE_CONNECT_API_KEY_ID |
ASC API Key ID | App Store Connect |
APP_STORE_CONNECT_ISSUER_ID |
ASC Issuer ID | App Store Connect |
APP_STORE_CONNECT_API_KEY_BASE64 |
AuthKey (.p8) の Base64 | App Store Connect |
EXPO_PUBLIC_* |
アプリの環境変数(Clerk 等) | 自プロジェクト |
証明書・プロファイルの取得コマンド
cd src/mobile # Expoプロジェクトのルート
npx eas credentials
# → iOS → production → Credentials.json: Update credentials.json with values from EAS servers
# これで credentials/ios/dist-cert.p12 と profile.mobileprovision が生成される
Base64 エンコードして GitHub Secrets に登録
# ⚠️ 改行混入を防ぐため base64 -w0 を必ず使う
base64 -w0 credentials/ios/dist-cert.p12 | gh secret set DISTRIBUTION_CERT_BASE64
base64 -w0 credentials/ios/profile.mobileprovision | gh secret set PROVISIONING_PROFILE_BASE64
# AuthKey_XXXXXXXXXX.p8 も同様
base64 -w0 AuthKey_XXXXXXXXXX.p8 | gh secret set APP_STORE_CONNECT_API_KEY_BASE64
# その他の平文Secret
gh secret set APPLE_TEAM_ID --body "ABCDE12345"
gh secret set PROVISIONING_PROFILE_NAME --body "*[expo] com.example.yourapp AppStore"
gh secret set APP_STORE_CONNECT_API_KEY_ID --body "XXXXXXXXXX"
gh secret set APP_STORE_CONNECT_ISSUER_ID --body "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
重要: .p12 や .p8 を .gitignore に追加しておくことを忘れずに。誤コミットしないよう credentials/ フォルダごと無視するのが安全です。
GitHub Actions ワークフロー YAML
.github/workflows/ios-build.yml にこのファイルを配置します。コピペ可能な形にしてあります(プレースホルダは自分のプロジェクトに合わせて置換してください)。
name: iOS Production Build & Submit
on:
workflow_dispatch:
inputs:
submit:
description: 'Submit to App Store after build'
required: false
default: 'true'
type: choice
options:
- 'true'
- 'false'
jobs:
build:
runs-on: macos-26 # ⚠️ 2026/4/28 以降はXcode 26必須なので macos-26 を使う
timeout-minutes: 60
defaults:
run:
working-directory: src/mobile # Expoプロジェクトのパス(プロジェクトルートなら削除)
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install dependencies
run: npm install
- name: Setup Expo
uses: expo/expo-github-action@v8
with:
eas-version: latest
token: ${{ secrets.EXPO_TOKEN }}
- name: Set environment variables
run: |
echo "EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY=${{ secrets.EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY }}" >> .env
echo "EXPO_PUBLIC_API_URL=${{ secrets.EXPO_PUBLIC_API_URL }}" >> .env
# ↑ 自分のプロジェクトのEXPO_PUBLIC_*変数を全部ここに追加
- name: Prebuild iOS
run: npx expo prebuild --platform ios --clean
- name: Decode signing certificate & provisioning profile
env:
DISTRIBUTION_CERT_BASE64: ${{ secrets.DISTRIBUTION_CERT_BASE64 }}
DISTRIBUTION_CERT_PASSWORD: ${{ secrets.DISTRIBUTION_CERT_PASSWORD }}
PROVISIONING_PROFILE_BASE64: ${{ secrets.PROVISIONING_PROFILE_BASE64 }}
KEYCHAIN_PASSWORD: ci_keychain_password_2026
run: |
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
CERT_PATH=$RUNNER_TEMP/distribution.p12
echo -n "$DISTRIBUTION_CERT_BASE64" | base64 --decode -o $CERT_PATH
security import $CERT_PATH -P "$DISTRIBUTION_CERT_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security list-keychain -d user -s $KEYCHAIN_PATH
PP_PATH=$RUNNER_TEMP/profile.mobileprovision
echo -n "$PROVISIONING_PROFILE_BASE64" | base64 --decode -o $PP_PATH
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
PP_UUID=$(/usr/libexec/PlistBuddy -c "Print UUID" /dev/stdin <<< $(/usr/bin/security cms -D -i $PP_PATH))
cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles/$PP_UUID.mobileprovision
- name: Install CocoaPods
run: |
cd ios
pod install
- name: Build iOS archive
run: |
cd ios
xcodebuild -workspace YourApp.xcworkspace \
-scheme YourApp \
-configuration Release \
-archivePath $RUNNER_TEMP/yourapp.xcarchive \
-sdk iphoneos \
archive \
CODE_SIGN_STYLE=Manual \
PROVISIONING_PROFILE_SPECIFIER="${{ secrets.PROVISIONING_PROFILE_NAME }}" \
CODE_SIGN_IDENTITY="Apple Distribution" \
DEVELOPMENT_TEAM="${{ secrets.APPLE_TEAM_ID }}"
- name: Export IPA
env:
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
PP_NAME: ${{ secrets.PROVISIONING_PROFILE_NAME }}
run: |
cat > $RUNNER_TEMP/ExportOptions.plist << EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>method</key>
<string>app-store-connect</string>
<key>teamID</key>
<string>${APPLE_TEAM_ID}</string>
<key>uploadBitcode</key>
<false/>
<key>uploadSymbols</key>
<true/>
<key>signingStyle</key>
<string>manual</string>
<key>provisioningProfiles</key>
<dict>
<key>com.example.yourapp</key>
<string>${PP_NAME}</string>
</dict>
</dict>
</plist>
EOF
xcodebuild -exportArchive \
-archivePath $RUNNER_TEMP/yourapp.xcarchive \
-exportOptionsPlist $RUNNER_TEMP/ExportOptions.plist \
-exportPath $RUNNER_TEMP/export
- name: Upload IPA as artifact
uses: actions/upload-artifact@v4
with:
name: yourapp-ios
path: ${{ runner.temp }}/export/*.ipa
- name: Cleanup keychain
if: always()
run: security delete-keychain $RUNNER_TEMP/app-signing.keychain-db || true
ビルド実行 → IPA ダウンロード → EAS Submit
ワークフローを手動トリガーします。
# 1. ビルド実行(submit=false を指定して IPA 生成のみ)
gh workflow run ios-build.yml --field submit=false
# 2. 実行中のワークフローを監視
gh run watch
# 3. 完了後、Run ID を取得
gh run list --workflow=ios-build.yml --limit 1
# 4. IPA アーティファクトをダウンロード
gh run download <RUN_ID> -n yourapp-ios -D /tmp/yourapp-ipa
# 5. EAS Submit で App Store Connect にアップロード(対話モードで Apple ID ログイン)
cd src/mobile
npx eas submit --platform ios --path /tmp/yourapp-ipa/YourApp.ipa
eas submit は Apple ID ログインを対話的に求めてきます。ここは EAS がよしなにやってくれるので任せてOKです。
アップロード完了後、5〜10分で App Store Connect の TestFlight タブにビルドが processing 完了した状態で現れます。あとは通常通り審査提出すれば完了です。
ハマったポイント(★経験談)
この解決策に至るまでに踏み抜いた地雷を共有します。
1. altool によるアップロードは諦めた方がいい
当初、GitHub Actions 内で xcrun altool --upload-app を使って直接 App Store Connect にアップロードしようとしました。しかし、ASC API Key の認証で以下のエラーが連発。
Error: CryptoKitError.underlyingCoreCryptoError(error: -7)
.p8 ファイルの Base64 エンコードを何度もやり直したり、Key ID・Issuer ID を確認したりしましたが解決せず。最終的に eas submit に切り替えたら一発で通りました。
教訓: ビルドは GitHub Actions、アップロードは EAS Submit、というハイブリッドが最適解。altool で消耗する意味はないです。
2. AuthKey .p8 の Base64 エンコードで改行混入
これが altool 失敗の一因でもありました。
# ❌ NG: 改行が混入してデコード失敗
base64 AuthKey_XXXXXXXXXX.p8 | gh secret set APP_STORE_CONNECT_API_KEY_BASE64
# ✅ OK: -w0 で改行を禁止
base64 -w0 AuthKey_XXXXXXXXXX.p8 | gh secret set APP_STORE_CONNECT_API_KEY_BASE64
Linux の base64 はデフォルトで76文字ごとに改行を入れます。これを GitHub Secrets に突っ込むと、復元時に改行コード混じりのバイナリになってデコードに失敗します。
3. Xcode workspace 名が app.json の name と一致しない
xcodebuild -workspace に渡す名前は、app.json の name(表示名)とは一致しません。たとえば name にスペースや日本語が含まれていても、Expo はそれをそのままファイル名にはせず、内部ルールで英数字のみに正規化します。
正解は app.json の ios.bundleIdentifier でも slug でもなく、expo prebuild が生成した ios/ フォルダ内の実際のファイル名です。
npx expo prebuild --platform ios --clean
ls ios/*.xcworkspace # ← ここで実際の名前を確認
たとえば name が "My Cool App" でも、生成されるファイル名は MyCoolApp.xcworkspace のようにスペースが除去された形になります。Expo の正規化ルールは完璧に推測できないので、一度ローカルで prebuild して実際のファイル名を確認するのが確実です。確認した名前をワークフローの xcodebuild -workspace と -scheme に指定してください。
4. Xcode バージョン要件(2026年4月28日以降)
これは全 Expo / iOS 開発者が影響を受ける要件なので絶対に押さえておいてください。
Apple は 2026年4月28日以降、iOS 26 SDK(Xcode 26)以降でビルドされたアプリしか新規申請・アップデートを受け付けない方針を発表しています。
-
macos-14(Xcode 15) → NG -
macos-15(Xcode 16) → NG -
macos-26(Xcode 26.2) → ✅ OK
GitHub Actions でビルドしている場合、runs-on: macos-26 を指定する必要があります。EAS Build を使っている場合は Expo 側が自動で対応してくれます。
私はこの要件を知らずに初回ビルドを macos-15 で走らせてしまい、App Store Connect にアップロード後に警告が出ていました。処理はされますが、期限後は蹴られます。
注意事項
credentials を絶対にコミットしない
# .gitignore に追加
credentials/
credentials.json
*.p12
*.p8
*.mobileprovision
.env
.env.*
GitHub Secrets で管理するなら、ローカルにあるファイルはいつ誤コミットするか分かりません。.gitignore に入れるのが最初の一歩です。
GitHub Actions 無料枠の確認
# 現在の使用量を確認
gh api /user/settings/billing/actions
Private リポジトリの macOS ランナーは 10倍係数なので、実質月200分です。それを超えると Public にするか、有料プランに切り替える必要があります。
まとめ
| 項目 | 値 |
|---|---|
| 解決策 | GitHub Actions macOS ランナー + EAS Submit のハイブリッド |
| コスト | 無料(Expo Production $19/月 を回避) |
| 初期セットアップ時間 | 約1〜2時間(Secrets 登録含む) |
| 1ビルド所要時間 | 約25〜30分 |
| 月のビルド回数 | GitHub Actions 無料枠内で約6〜7回 |
| 必要な前提 | Apple Developer 登録済 + EAS で credentials 発行済 |
感想
正直、最初は「EAS Build 枠切れ」の時点で諦めて有料プラン加入を検討していました。しかし、EAS Build と EAS Submit が別サービスであるという事実に気づいた瞬間に全てが繋がりました。
- ビルドだけ他の手段で行えばいい
- アップロードの面倒は EAS に任せていい
Expo は「全部やってくれるプラットフォーム」ですが、部分的に切り出して使える柔軟性もあります。同じ問題に悩む方の参考になれば嬉しいです。
参考リンク
- Expo EAS Build Pricing
- EAS Submit Documentation
- GitHub Actions macOS runner versions
- Apple: iOS 26 SDK Requirements
- Expo prebuild



