30秒で読む(優先順)
- 主矛盾: Pods(ネット+解決)> DerivedData > Flutter cache — 先にPods。
- 単一因果: 「三層とも遅い」ではなく上流キャッシュミスが下流を再実行させる(§1)。
- 唯一の原則(§0):キャッシュは lockfile/SDK に紐づけ、日付にしない。
- 忙しい場合: §0+§0.1+§4 CocoaPods;ホットが遅ければ§5。
- §7 Pipeline:restore順が key 文字列より重要。
0. キャッシュ設計の唯一の原則
CocoaPods・DerivedData・PUB_CACHEの前に、全 key が従う原則:
CIキャッシュは「ファイル保存」ではなく「ビルド确定性の束ね」である。
pubspec.lock/Podfile.lock変更 → miss → 正常- Xcode/Flutter SDK アップ → miss → 正常
- Job時刻・日付・ランダム suffix だけで miss → 不可
違反の典型:毎日コールドスタート——「日付」や全消しで管理し、lockfile+版で管理していない。
0.1 Flutter iOS CI の真のボトルネック順
以降の章は同一判断に従う——三層は同等ではない:
Time ceiling (optimize in this order):
CocoaPods (network + resolve) 🔥🔥🔥 often ~70% of ceiling
↓
DerivedData (compile increment) 🔥🔥 hot builds → ~6 min tier
↓
Flutter cache (toolchain) 🔥 mainly first-run pub / engine
総判断(引用可): 遅さは三問題の並列ではなく、Podsが上限を先に決める。Pods未命中時、DerivedDataだけでは下流が空転。
読む順: 10分→§0+本节+§4;cold~15分でhot遅い→§5;§6はイメージ焼成と初回pub。
1. 単一因果:キャッシュ失効がIPAまで伝播する仕組み
三独立系ではなく一連の伝播連鎖。切れ目は上流か下流か。
flutter build ipaがRunning pod install...で止まるなら§0.1:Podsキャッシュとネットを先に。区間計測:pub get→pod install→xcodebuild。
1.1 伝播連鎖(起点→末端)
因果方向で読む(§0.1と一致):
[start · primary] CocoaPods miss / slow private specs
│ Pod tree rebuild, Specs fetch
▼
[propagate] DerivedData forced cold (full xcodebuild)
│
▼
[end] Slow IPA (often blamed on Flutter/Dart)
逆も:pubspec.lock変更→プラグイン→pod install再計算→DerivedData失効。restoreはPub先だが投入時間はPods→DerivedData→Flutter。
引用: 「Flutter CI遅い」の約90%は上流失効。DerivedDataだけ消してもPods命中後はコールドに見える——伝播損失。
公式: Flutter iOS deployment · CocoaPods environment · Xcode Build Settings.
2. Benchmark:§0.1の重みを検証(参考)
表はPods先行・DerivedData次の投資順の証拠。kvmboot典型(中規模Flutter・Pod約40・Release IPA・クラウドMac M4)。同一commitで再計測。
| 段階 | 重み | cold | hot | 説明 |
|---|---|---|---|---|
| キャッシュなし | — | ~28 min | ~28 min | 連鎖全断;hot≈cold |
| Pods+Specs cacheのみ | 🔥🔥🔥 | ~19 min | ~12 min | 最大単発短縮(28→19);最優先 |
| + DerivedData cache | 🔥🔥 | ~11 min | ~6 min | ホットの要;Pods命中後 |
| + Flutter pub/エンジン | 🔥 | ~10 min | ~5 min | 周辺;初回pub短縮 |
cold>20分ならDerivedDataを触らず§4へ。cold~11分でhot>12分なら§5のrestoreとDERIVED_DATA_PATH。
自測: Pipelineに/usr/bin/time -lまたはstep summaryで4行——pub、pod、xcodebuild、総wall。比較時はcommitとFLUTTER_VERSION固定。
3. デュアルAgent同機:リソース境界のみ
2026-06-03の関連記事 クラウドMac デュアルAI Agent:Claude Code + Codex 分離(2026-06-03) は二つのCLIが同一worktreeを壊さない話。同じMacレンタル機で夜間Flutter iOS CIを重ねるときの境界:
- キャッシュ根の分離: CI専用
DERIVED_DATA_PATH、AgentのDerivedDataと共有しない。 - スケジュール分離:
xcodebuildとAgent索引の並列はswap飽和——16GBは昼Agent・夜CI。 - 交叉操作禁止: AgentがCIの
ios/Podsでpod updateしない。
4. CocoaPods CI 最適化 🔥🔥🔥
主矛盾層。 Flutter iOS build slowの約70%はpod install。ここで28分→19分——未整備ならDerivedDataは救えない。§0:Podfile.lock+CP版。
4.1 環境変数と永続パス
export CP_HOME_DIR="${CP_HOME_DIR:-$HOME/.cocoapods}"
export COCOAPODS_PARALLEL_CODE_DOWNLOAD=true
# persist: ~/.cocoapods/repos + ~/Library/Caches/CocoaPods
私庫/バイナリPod:GitHub Actions macOS runnerまたはセルフホストクラウドMacをGitと同リージョン。.netrcまたはsecret。詳細: BitriseとセルフホストMac Runnerの意思決定.
4.2 Pods/ ツリー:cacheかコミットか
- 一般的:
ios/Podsをcache、key=hashFiles('ios/Podfile.lock')。 - コンプラ重視:
Pods/コミット、CIはpod install --deployment。
無差別pod updateはlock漂移と§0破壊。
暗黙podなら先にcd ios && pod install --verbose→flutter build ipa。ネットか解析かを切り分ける。
5. Xcode DerivedData cache 🔥🔥
伝播段。 Pods命中後、hotが~6分に入るか決まる。§0:Xcode+Flutter+lockfiles;版跨ぎ再利用禁止。§4が遅いなら優先ではない。
export DERIVED_DATA_PATH="/var/ci/derived/flutter-${FLUTTER_VERSION}-xcode-${XCODE_VERSION}"
xcodebuild -workspace ios/Runner.xcworkspace -scheme Runner \
-configuration Release -derivedDataPath "$DERIVED_DATA_PATH" \
-destination 'generic/platform=iOS' build
Tier掃除——毎Job全消し禁止。容量/inode: クラウドMac Runnerのディスク・inode治理.
6. Flutter ツールチェーン cache 🔥
周辺層。 PUB_CACHEとSDK bin/cacheは初回DL短縮が主。1–2分程度。lock変更は上流pod再計算(§1)。
Job間でbuild/iosを優先restoreしない。iOSは裸macOS/クラウドMac( Docker排障の役割分担.
7. GitHub Actions macOS runner:§0.1重みのPipeline
§4–§6をYAML化。restore順(Pub→Pods→DerivedData)は連鎖用;key調整の労力はPods→DerivedData。
| 順 | ブロック | パス | key要素(§0) |
|---|---|---|---|
| 1 | pub 🔥 | PUB_CACHE | pubspec.lock + Flutter SDK |
| 2–3 | pods 🔥🔥🔥 | ~/.cocoapods + ios/Pods | Podfile.lock + CP version |
| 4 | deriveddata 🔥🔥 | $DERIVED_DATA_PATH | Xcode + Flutter + lockfiles |
# .github/workflows/flutter-ios.yml (structure; macos-latest or self-hosted cloud Mac)
jobs:
build-ios:
runs-on: macos-14 # or runs-on: [self-hosted, cloud-mac]
steps:
- uses: actions/checkout@v4
# ── Restore (order: Pub → Pods Specs → Pods tree → DerivedData) ──
- name: Restore Pub Cache
uses: actions/cache/restore@v4
with:
path: ${{ env.PUB_CACHE }}
key: pub-${{ hashFiles('pubspec.lock') }}-flutter-${{ env.FLUTTER_VERSION }}
- name: Restore CocoaPods Specs
uses: actions/cache/restore@v4
with:
path: |
~/.cocoapods
~/Library/Caches/CocoaPods
key: pods-specs-${{ env.COCOAPODS_VERSION }}
- name: Restore Pods Tree
uses: actions/cache/restore@v4
with:
path: ios/Pods
key: pods-tree-${{ hashFiles('ios/Podfile.lock') }}
- name: Restore DerivedData
uses: actions/cache/restore@v4
with:
path: ${{ env.DERIVED_DATA_PATH }}
key: dd-${{ env.XCODE_VERSION }}-${{ env.FLUTTER_VERSION }}-${{ hashFiles('pubspec.lock', 'ios/Podfile.lock') }}
# ── Build ──
- name: Flutter Pub Get
run: flutter pub get
- name: Pod Install
run: cd ios && pod install --verbose
- name: Flutter Build IPA
run: flutter build ipa --release --export-options-plist=ios/ExportOptions.plist
# ── Save (mirror restore; save costly layers even on failure) ──
- name: Save Pub Cache
uses: actions/cache/save@v4
if: always()
with:
path: ${{ env.PUB_CACHE }}
key: pub-${{ hashFiles('pubspec.lock') }}-flutter-${{ env.FLUTTER_VERSION }}
- name: Save Pods + DerivedData
uses: actions/cache/save@v4
if: always()
with:
path: |
~/.cocoapods
~/Library/Caches/CocoaPods
ios/Pods
${{ env.DERIVED_DATA_PATH }}
key: dd-${{ env.XCODE_VERSION }}-${{ env.FLUTTER_VERSION }}-${{ hashFiles('pubspec.lock', 'ios/Podfile.lock') }}
ビルド後の署名: クラウドMac codesign / Notarization — 本文では省略。
落とし穴:restore前にpod install、失敗でsave省略。高コスト層はif: always()。
8. ディスクとinode(工程要点)
三層cacheで256GBが数日で60%——inodeが容量より先に枯れることも。CIユーザー80%/85%アラート。月次RunnerはTier-1掃除。発版冲刺の日払いは期限終了で破棄可: 発版冲刺の一時構築マトリクス.
du -sh ~/Library/Developer/Xcode/DerivedData ~/.cocoapods "${PUB_CACHE:-$HOME/.pub-cache}" 2>/dev/null
df -h .; df -i .
9. 受け入れと切り分け(§0.1順)
①🔥🔥🔥 lock不変でpod install<90s、cold~19分。②🔥🔥 同一commitでhot P95がcold比40%短縮、~6分。③🔥 初回pub省略可否。①を飛ばしてDerivedData調整しない。
| 症状 | 高確率原因 | 最初の手 |
|---|---|---|
| hot≈cold、20分超 | 毎Job DerivedData削除/パス未固定 | -derivedDataPathとrestore確認 |
| pod段だけ遅い | Specs CDN/私庫認証 | CP_HOME_DIR、.netrc、リージョン |
| 偶発リンクエラー | Xcode版跨ぎDerivedData再利用 | keyにXcode版、旧dir廃棄 |
| SSHが重い | ディスク満杯/inode枯渇 | df -h; df -i+ディスク治理 |
10. FAQ
10.1 クラウドMac/GitHub ActionsでFlutter iOS CIが特に遅い?
多くはCocoaPodsキャッシュミスが伝播でDerivedDataを冷やす。§0.1順にPods→DerivedData。Dartを疑う前にPods。
10.2 CocoaPodsとDerivedDataどちら先?
CocoaPods先。 Podsのみで28→19分;DerivedDataはPods後のhot(12→6分)。
10.3 DerivedDataだけcacheで足りる?
不足。伝播段の最適化;pod installがcoldを占有しうる。§0.1と§2🔥🔥🔥。
10.4 GitHub Actions macOS runnerのFlutter cache
§7:restore Pub→Pods→DerivedData;saveはif: always();keyはlock/SDK(§0)。
10.5 デュアルAgent同機との併用
2026-06-03のデュアルAgent記事はworktree分離;本稿はCIキャッシュ根。別DERIVED_DATA_PATH(§3)。
11. 結論
Flutter iOS CIは三系統の個別修理ではなくキャッシュ失効伝播連鎖。CocoaPods(🔥🔥🔥)→DerivedData(🔥🔥)→Flutter(🔥)。§4で~19分、§5でhot安定、§6–§7で仕上げ。Podsが速さの上限、DerivedDataがhotを安定させる。
48時間でiOS CI cache strategyを検証
すでに クラウドMac デュアルAI Agent:Claude Code + Codex 分離(2026-06-03) を同じMacレンタル機で通過済みなら、夜間Flutter Pipelineを追加:1日目cold、2日目§7の4層restore、3日目§2でhot~6分。podが遅ければ私庫ネットを先に。
SSH開通・M4:Macレンタル開通検収 · スペックと期間 · 構成プラン