Angebot

Warum Flutter iOS CI auf einem Cloud Mac langsamer wird: Drei Cache-Ebenen als Ursache

CI Flutter · iOS-Build
2026-06-04 ~15 Min.

Eine These: Flutter iOS CI ist Cache-Invalidierungs-Propagation, nicht drei unabhängige Systeme. Primär CocoaPods, dann DerivedData. Nach §0 und §0.1 wissen Sie, was auf gemietetem Mac oder GitHub Actions macOS runner zuerst dran ist.

30 Sekunden (Priorität)

  1. Hauptengpass: Pods (Netz + Auflösung) > DerivedData > Flutter cache — zuerst Pods.
  2. Einzelursache: nicht „alle drei Ebenen langsam“, sondern Upstream-Miss zwingt Downstream-Neuarbeit (§1).
  3. Eine Regel (§0): Cache an Lockfiles/SDK, nicht an Datum.
  4. Wenig Zeit: §0+§0.1+§4 CocoaPods; Hot-Build langsam → §5.
  5. §7: Restore-Reihenfolge schlägt clevere Keys.
Flutter iOS CI Build und Cache-Tuning auf Cloud Mac
Titelbild illustrativ; Benchmark-Bedingungen in der Tabelle.

0. Das eine Cache-Prinzip

Vor CocoaPods, DerivedData, PUB_CACHE gilt für alle Keys:

CI-Cache bindet Build-Determinismus — nicht „Dateien lagern“.

  • pubspec.lock / Podfile.lock ändern → Miss → erwartet
  • Xcode/Flutter-SDK-Upgrade → Miss → erwartet
  • Job-Zeit, Datum, Zufallssuffix allein → nicht

Symptom: täglicher Kaltstart — Caches per Datum oder Vollwipe statt Lockfile+Version.

0.1 Echte Engpass-Reihenfolge

Alle Abschnitte folgen einem Urteil — Ebenen sind ungleich:

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

Urteil: Langsamkeit ist selten drei parallele ProblemePods deckeln zuerst; bei Pods-Miss hilft DerivedData nur downstream ins Leere.

Lesepfad: 10 Min → §0+§0.1+§4; Cold ~15 Min, Hot langsam → §5; §6 für Image und erstes pub.

1. Einzelursache: Propagation bis IPA

Keine drei Subsysteme — eine Kette. Bruch upstream oder downstream?

Hängt flutter build ipa bei Running pod install..., §0.1: Pods-Cache/Netz vor Xcode-Flags. Messen: pub → pod → xcodebuild.

1.1 Kette

In Kausalrichtung lesen:

  [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)

Rückwärts: pubspec.lock → Plugins → pod → DerivedData weg. Restore: Pub zuerst; Debug-Budget: Pods → DerivedData → Flutter.

Zitat: ~90 % „Flutter CI langsam“ = Upstream-Kette, nicht Compiler.

Offiziell: Flutter iOS deployment · CocoaPods environment · Xcode Build Settings.

2. Benchmark: §0.1-Gewichte

Tabelle belegt Pods zuerst, DerivedData danach. kvmboot-Typisch (mittleres Flutter, ~40 Pods, Release, Cloud Mac M4); gleicher Commit.

Stufe Gewicht cold hot Hinweis
Kein Cache ~28 min ~28 min Kette offen; Hot≈Cold
Nur Pods+Specs 🔥🔥🔥 ~19 min ~12 min Größter Einzelschritt; zuerst
+ DerivedData 🔥🔥 ~11 min ~6 min Hot-Build; nach Pods-Treffer
+ Flutter pub/Engine 🔥 ~10 min ~5 min Rand; erstes pub

Cold >20 Min → §4, nicht §5. Cold ~11, Hot >12 → §5 restore.

Selbsttest: Vier Zeilen wall time; Commit und FLUTTER_VERSION fixieren.

3. Dual-Agent auf einem Host: Grenzen

Begleitartikel vom 2026-06-03: Cloud Mac Dual-AI-Agent: Claude Code + Codex-Isolation (2026-06-03)zwei CLIs, ein Worktree intakt. Mit Nacht-Flutter-CI auf demselben gemieteten Mac:

  • Getrennte Cache-Wurzeln: CI-DERIVED_DATA_PATH, nicht Agent-DerivedData.
  • Getrennte Planung: paralleles xcodebuild + Indexierung → Swap; 16 GB: Agent tags, CI nachts.
  • Kein Kreuz: kein pod update auf CI-ios/Pods.

4. CocoaPods CI 🔥🔥🔥

Primärschicht. ~70 % Flutter iOS build slow in pod install. 28→19 Min hier — sonst rettet DerivedData nicht. §0: Podfile.lock.

4.1 Umgebung

export CP_HOME_DIR="${CP_HOME_DIR:-$HOME/.cocoapods}"
export COCOAPODS_PARALLEL_CODE_DOWNLOAD=true
# persist: ~/.cocoapods/repos + ~/Library/Caches/CocoaPods

Private Specs: GitHub Actions macOS runner / Self-hosted Cloud Mac in Git-Region. Bitrise vs Self-hosted Mac Runner.

4.2 Pods/-Baum

  • Üblich: ios/Pods cachen.
  • Compliance: Pods/ committen, --deployment.

Kein blindes pod update.

Explizit pod install --verbose vor flutter build ipa.

5. DerivedData 🔥🔥

Nach Pods-Treffer Hot ~6 Min.

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-Sweep; Disk: Runner-Disk- und Inode-Governance.

6. Flutter-Toolchain 🔥

Erstlauf-Downloads.

Nicht build/ios cross-job; Docker-Aufteilung.

7. GitHub Actions Pipeline

§4–§6 als YAML.

Nr. Block Pfade Key (§0)
1pub 🔥PUB_CACHEpubspec.lock + Flutter SDK
2–3pods 🔥🔥🔥~/.cocoapods + ios/PodsPodfile.lock + CP version
4deriveddata 🔥🔥$DERIVED_DATA_PATHXcode + 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') }}

Signierung: codesign / Notarization.

Fallen: pod vor restore; kein save bei Fail — if: always().

8. Disk/Inode

Drei Ebenen → 60 % auf 256 GB; inode oft zuerst. Release-Sprint-Matrix.

du -sh ~/Library/Developer/Xcode/DerivedData ~/.cocoapods "${PUB_CACHE:-$HOME/.pub-cache}" 2>/dev/null
df -h .; df -i .

9. Abnahme

① pod <90s ② Hot −40 % ③ pub optional.

Symptom Ursache Aktion
Hot≈Cold 20+ DerivedData jedes Job Pfad/restore
nur pod CDN/Auth CP_HOME_DIR, .netrc
Link-Fehler Xcode-Mix Key+Xcode
SSH hängt Disk/inode df + Governance

10. FAQ

10.1 Warum so langsam auf Cloud Mac?

CocoaPods-Miss → Kette. Pods zuerst.

10.2 Pods oder DerivedData?

Pods zuerst.

10.3 Nur DerivedData?

Nein.

10.4 Runner-Cache?

§7; if: always().

10.5 Dual-Agent?

2026-06-03: worktree; hier CI-Cache.

11. Fazit

CocoaPods (🔥🔥🔥) → DerivedData (🔥🔥) → Flutter (🔥).

48h validieren

Nach Cloud Mac Dual-AI-Agent: Claude Code + Codex-Isolation (2026-06-03) Nacht-Pipeline auf gleichem Mac mieten.

Onboarding: Mac-mieten-Checkliste · Spezifikation · Tarife