MSA 中身解剖 ハブ の 深掘り 2本目。 pasha (レシート 経費管理 iOS) で 踏んだ 「fastlane + Apple 周辺 の 落とし穴」 の 話。
0. 何 が 起きた か
3ヶ月 前、 pasha の 審査提出 を GitHub Actions の CI から 自動化 しようとした。 fastlane の 公式 ドキュメント を 読んで:
Apple recommends using App Store Connect API keys for CI/CD. They're scoped, revocable, and don't require 2FA.
「これ や」 と 思って API キー を 発行、 fastlane の Appfile に 設定、 fastlane deliver --submit-for-review を CI で 走らせた。
✗ Error: Cannot use App Store Connect API for App Review submissions
は?
1. ドキュメント を 読み直す
App Store Connect API の 公式 リファレンス (developer.apple.com/app-store-connect/api/) を 開く。 全 endpoint を grep:
GET /v1/apps
GET /v1/builds
POST /v1/builds/{id}/relationships/individualTesters
POST /v1/preReleaseVersions
... (200+ endpoints)
"submission" や "review" を含む endpoint が 1つ も ない。 App Store Connect API は 「アプリ の メタデータ 管理」 と 「TestFlight 配信」 は できるが、 「正式 審査提出」 だけは できない。
これ、 公式 docs の どこ にも 「審査提出 は API 経由 では 出来ません」 と 明記 されてい ない。 endpoint が 単に 「存在 しない」 だけ。 「無い こと の 証明」 を 自力 で やる ハメに なった。
2. fastlane の deliver ソース を 読む
fastlane の deliver action を grep:
$ git clone https://github.com/fastlane/fastlane
$ rg "submit_for_review" fastlane/deliver/
fastlane/deliver/lib/deliver/submit_for_review.rb の 中:
def submit
# Use spaceship (Apple ID auth) — no API token path
app = Spaceship::Tunes::Application.find(@app_identifier)
app.create_submission(...)
end
spaceship 経由 で Apple ID + パスワード + 2FA で 認証 する path しか ない。 これ で 確信。
ちなみに spaceship は Apple の 非公開 internal API を リバース エンジニアリング した ライブラリで、 Apple ID 認証 を ブラウザ から の リクエスト として 偽装 して 通す。 ABI も 仕様 も 約束 されて ない。 突然 動か なくなる ことが あり、 過去 何度も Apple が UI を 変えるたび に fastlane コミュニティ が 大慌て で 直してきた 経緯 が ある。
3. 1Password 経由 で Apple ID auth に 戻す
CI で Apple ID 認証 を 通す には 2FA を 自動化 する 必要 が ある。 オプション:
spaceauthで セッション cookie を 取得 して 環境変数 に 入れる — セッション が 数日 で 切れる ので CI が 突然 落ちる- 専用 の Apple ID を 作って 2FA を 信頼デバイス で 受ける — でも GitHub Actions の runner に SMS を 紐付けられ ない
- 1Password Service Account の SSH/CLI 経由 で 2FA code を 取り出す — fastlane に
FASTLANE_SESSIONを 注入
採用 した の は (3)。 ローカル の Mac で:
$ op signin
$ op item get "Apple ID — pasha" --otp
123456
を fastlane spaceauth -u $APPLE_ID の 流れで 取得、 結果 を GitHub Actions の secret に 投入。 セッション は 30日 持つ ので、 月初 に 手動 更新。
4. fastlane の Appfile
# Before (API key approach — failed)
app_store_connect_api_key(
key_id: ENV['ASC_KEY_ID'],
issuer_id: ENV['ASC_ISSUER_ID'],
key_filepath: 'AuthKey.p8'
)
# After (Apple ID approach — works)
apple_id ENV['APPLE_ID']
team_id ENV['TEAM_ID']
# FASTLANE_SESSION is read from env automatically
これで fastlane deliver --submit-for-review が GitHub Actions から 通る ように なった。
5. 副産物 — 審査リジェクト の 理由 を 取る も spaceship 経由
審査 が rejected された 場合、 拒否 理由 を プログラム から 取り出したい こと が ある。 こちら も App Store Connect API には 取り出す endpoint が ない。 spaceship 経由 で 取る しか ない:
require 'spaceship'
Spaceship::ConnectAPI.login(ENV['APPLE_ID'], ENV['APPLE_PASSWORD'])
app = Spaceship::ConnectAPI::App.find('com.enabler.pasha')
sub = app.get_app_store_review_submissions(filter: { state: 'IN_REVIEW' }).first
puts sub.review_notes # ← ここに human-readable な reject 理由
これ 知らなかった ら 「リジェクト 来た → App Store Connect の Web UI 開く → 拒否 通知 を copy paste → 修正」 という 手動 ループ から 抜けられない。
6. Pasha の fastlane Gemfile.lock の Dependabot 騒動 (続き)
先週 (2026-05-19) の OSS-stop の 流れ で、 pasha の Gemfile.lock にも Dependabot 警告 が 4件 出てきた:
- jwt: CVE-2026-45363 (空鍵 HMAC バイパス)
- addressable: CVE-2026-35611 (ReDoS)
- json: CVE-2026-33210 (format-string injection)
- faraday: CVE-2026-25765 (SSRF)
bundle update jwt addressable json faraday --conservative で 3件 は patch できた。 でも jwt だけ patch できなかった。 理由 が 笑える: fastlane 2.234.0 (最新版) が jwt < 3 を pin している。 jwt 3.x が CVE-fix版 だが、 fastlane が 来てくれない 限り バンプ できない。
PR #1 は 3件 fix + jwt は upstream の fastlane が pin を 緩める まで 保留 と 書いて merge した。 release tooling 用 の Ruby bundle なので 攻撃面 は 限定的、 という 判断。
7. 学んだこと
- 「公式 が API を 用意 してる = 全機能 が 使える」 は 誤り。 endpoint が 単に 存在 しない 操作 (App Store の submit_for_review) が ある。 docs に 「無い」 と 書いて ない だけ。
- 不足 を 知る には 「使われて いる 既存 ライブラリ の ソース を 読む」 のが 早い。 fastlane の deliver/spaceship を 読まなかった ら 半日 ハマって いた。
- Apple の internal API を リバース した spaceship に 頼る しか ない 操作 が 存在 する — submit_for_review、 reject 理由 取得、 IAP プロモコード 一括発行 etc。 公式 API では カバー されない。
- 依存 update の patch 漏れ は 理由 を 明記 して merge する。 「全部 fix」 を 装う より 「3/4 fix、 1 は 待ち」 と 書く ほうが 健全。
これ 全部、 fastlane Apple Dev 界隈 では 「皆 が 知っていて 誰も documented してい ない」 暗黙知 と 化して いる。 pasha の CI ファイル と Gemfile を 読むと、 ハマった 順序 が commit log で 追える。
👉 Tシャツ買って pasha + 残り 20 リポ を 読む — ¥4,900〜
明日 は nanobot — 公開 issue として 放置した prompt injection と CORS reflect、 直すまで。