VPSSpark Blog
← Retour au journal

Deux Cloud Mac suffisent-ils pour la CI Flutter d’une équipe ?

Notes serveur · Cloud Mac CI #9 · 2026.06.08 · ~12 min

Recherches fréquentes : Flutter CI macOS · pipeline iOS Flutter · runner self-hosted Cloud Mac

Développeur Flutter sur Mac, contexte pipeline CI
La CI Flutter dual-platform coince sur iOS/macOS—deux Cloud Mac pour fast et archive.

VPSSpark Cloud Mac CI série #9. Question récurrente dans les équipes Flutter en France et en Europe : avec 8 à 15 développeurs, deux Cloud Mac suffisent-elles pour une CI stable ? Oui — à condition de séparer Android et iOS : les builds Android restent sur le ubuntu-latest hébergé par GitHub, côté macOS deux runners self-hosted répartis entre macos-fast et macos-archive, et la règle d’or : pas d’archive sur les PR. Ci-dessous : modèle de charge, couches de jobs, extrait de workflow et checklist de mise en prod. Diagnostic des files d’attente et branchement des runners : série #2 et retour d’expérience trois Cloud Mac pour un fort volume iOS CI.

1 · Conclusion contre-intuitive : le goulot Flutter CI n’est pas Dart

La plupart des CI Flutter en échec ne traînent pas sur flutter test, mais parce que les jobs iOS saturent les slots macOS — souvent après un flutter build ipa lancé par erreur sur une PR.

Le cross-platform laisse croire qu’un seul workflow suffit. En pratique, flutter analyze et les tests unitaires tournent aussi sous Linux ; macOS est indispensable pour CocoaPods, la compilation Xcode, la signature et l’export IPA. Si un job archive entre dans le chemin PR, un Cloud Mac reste bloqué 25–40 minutes et toutes les autres PR passent en Queued — le même piège que les équipes iOS natives ; formule de diagnostic : wait time >> run time (voir diagnostic file d’attente macOS runner).

Deux Cloud Mac ne sont donc pas « deux machines universelles », mais une topologie bi-pool volontaire : feedback rapide d’un côté, tâches release de l’autre. Les APK/AAB restent sur Linux — ne pas gaspiller les slots macOS sur Gradle.

2 · Modèle de charge : combien de parallélisme macOS pour 8–15 personnes ?

Référence : équipe Flutter moyennement active (1–2 PR par personne et par jour, nightly sur main, release hebdomadaire) :

12
Taille d’équipe type
~18
Jobs macOS / jour ouvré
2
Slots Cloud Mac

Sur le pool rapide (macos-fast) : analyze, tests, build simulateur iOS — objectif 8–14 minutes avec cache pub/DerivedData. Sur le pool archive (macos-archive) : IPA release + notarisation — 20–35 minutes, mais peu fréquent (main, tags, nightly). Pools mélangés : l’archive étouffe le fast ; séparés, le P95 d’attente PR reste souvent sous 10 minutes — suffisant pour le rythme de revue de code en équipe distribuée EU.

Au-delà de 15 personnes ou avec beaucoup de flavors en matrix monorepo, une troisième machine archive peut se justifier — planification comme dans le scénario 500 builds/jour, hors scope de ce démarrage à deux machines.

Plateforme Runner Job type Sur PR ?
Android ubuntu-latest (hébergé) flutter build apk/appbundle, tests Android Oui
iOS feedback rapide Cloud Mac #1 · macos-fast analyze, tests, build ios --simulator Oui
iOS release Cloud Mac #2 · macos-archive build ipa, notarisation, TestFlight Non

3 · Trois niveaux de jobs Flutter : alignés sur les CI Hard Rules

Mapper le pipeline Flutter sur L0/L1/L2 — cohérent avec le manuel on-call :

Niveau Contenu Flutter Pool Déclenchement PR ?
L0 dart format --set-exit-if-changed, flutter analyze, tests légers Linux ou macos-fast Oui
L1 Tests d’intégration, flutter build ios --simulator, widget tests macos-fast Oui · pas d’archive
L2 flutter build ipa, App Store Connect, notarisation macos-archive Non · main/tag/schedule uniquement

Trois règles comme en iOS natif : Rule 1 pas de L2 sur PR ; Rule 2 L2 dans un pool isolé ; Rule 3 le fast pool ne doit pas être bloqué par l’archive. Spécificité Flutter : beaucoup de L0 peut tourner sur Linux — tout lier à macos-fast gaspille la concurrence macOS. Sur PR : jobs Linux et macOS en parallèle ; macOS uniquement pour l’indispensable.

4 · Topologie bi-machine : rôles de Cloud Mac #1 et #2

Fig. 1 · Topologie type : deux Cloud Mac pour la CI d’une équipe Flutter

GitHub Actions · séparation PR / mainAndroid → ubuntu-latest
Cloud Mac #1 · macos-fastL0/L1 · 8–14 min/job
Cloud Mac #2 · macos-archiveL2 · main/tag uniquement

Cloud Mac #1 (pool rapide) : labels [self-hosted, macOS, macos-fast, flutter]. SDK Flutter figé (fvm ou pin dans l’image), Xcode, CocoaPods. Runner en service permanent avec démarrage auto ; cache pub et DerivedData chauds — jobs courts, fort débit.

Cloud Mac #2 (pool archive) : labels [self-hosted, macOS, macos-archive, flutter-release]. Isolation physique — jamais deux runners de pools différents sur le même Mac (sinon Rule 2 est vide). Certificats Distribution et clé API App Store Connect dans le Keychain ; branchement comme dans l’onboarding runner en conditions réelles. Avant release : flutter doctor -v et archive dry-run — ne pas polluer le fast pool.

Même région, même version majeure Xcode — moins de « fast vert, archive rouge ». Cloud Mac : image fixe, lien GitHub stable, pas d’UPS de bureau — adapté au CI 7×24. Sur les limites de sécurité des runners self-hosted : restreindre la machine archive à un repo ou une org.

5 · Exemple de workflow : séparation PR et main

Logique centrale de routage (pipeline complet : cache, secrets, artifacts en plus). Documentation CD Flutter pour la signature iOS ; certificats déjà dans le Keychain archive.

flutter-ci.yml (cœur)
jobs:
                  android-pr:
                    if: github.event_name == 'pull_request'
                    runs-on: ubuntu-latest
                    steps:
                      - run: flutter analyze && flutter test
                      - run: flutter build appbundle --release

                  ios-pr-fast:
                    if: github.event_name == 'pull_request'
                    runs-on: [self-hosted, macOS, macos-fast, flutter]
                    steps:
                      - run: flutter analyze
                      - run: flutter test
                      - run: flutter build ios --simulator --no-codesign

                  ios-release:
                    if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')
                    runs-on: [self-hosted, macOS, macos-archive, flutter-release]
                    steps:
                      - run: flutter build ipa --export-options-plist=ExportOptions.plist
                      # TestFlight / notarisation — L2 uniquement

ios-pr-fast n’inclut volontairement pas build ipa. Build appareil sur PR : uniquement via workflow_dispatch, toujours pool archive — pas en événement PR par défaut. Avec concurrency dans la syntaxe workflow, annuler les anciens runs PR — souvent ~20 % d’attente en moins.

6 · Checklist cache : quatre répertoires à persister

Deux Cloud Mac tiennent mieux avec un bon cache qu’avec le dernier CPU. Persister dans le home du runner (ou snapshot nightly) :

  • PUB_CACHE / ~/.pub-cache — deps Dart ; mise à jour incrémentale si pubspec.lock change
  • Répertoire SDK Flutterfvm fixe la version ; pas de flutter upgrade en CI
  • ios/Pods + cache CocoaPodspod install est souvent la longue traîne iOS
  • Xcode DerivedData — fort gain avec le même ios/Podfile.lock

Fast et archive ne partagent pas le même DerivedData — mélanger Debug/Simulateur et Release/Archive provoque des erreurs de link fantômes. Cache Gradle Android sur ubuntu-latest via Actions Cache ; épargner le disque Cloud Mac.

Baseline d’image
Chaque mois, image « propre » : bump minor Flutter + patch Xcode + pod repo update — valider d’abord sur archive, puis synchroniser le fast pool. Les PR quotidiennes réutilisent le cache ; pas de flutter pub upgrade par défaut en CI.

7 · Checklist mise en prod : de zéro à deux machines branchées

En 1–2 jours ouvrés pour un PoC :

  • 2 Cloud Mac même spec (M4 + 16 Go recommandé ; pics compile iOS souvent 12 Go+)
  • #1 : Flutter/Xcode/CocoaPods, runner macos-fast ; #2 : matériel de signature, runner macos-archive
  • Scinder les workflows : Android ubuntu-latest ; PR L1 seulement ; main/tag L2
  • Persister pub/DerivedData/Pods ; observer 3 jours P95 wait < 10 min
  • Si wait >> run persiste : vérifier L2 dans les PR avant d’ajouter des machines

PoC possible avec une Cloud Mac pour le fast pool ; archive temporairement sur macos-latest hébergé (accepter la file) — valider la logique, puis deuxième machine pour le SLA release. Cohérent avec « deux machines » : la seconde assure les releases, pas le feedback PR.

8 · FAQ

Tous les tests Flutter sur Linux — une Cloud Mac suffit ?

Sans compile macOS en PR, en théorie oui — la plupart des équipes lancent build ios --simulator pour les plugins natifs. Un Mac pour L1 passe ; l’archive doit rester isolée du feedback rapide, sinon les releases bloquent toutes les PR. Deux machines restent le choix sûr.

Deux Cloud Mac vs deux Mac mini au bureau ?

Topologie identique ; différence opérationnelle : Cloud Mac sans achat matériel, images snapshotables, bande passante GitHub souvent plus stable. Mac bureau si 7×24 et salle serveur ; équipes EU distribuées choisissent souvent Cloud Mac — voir aussi montée en charge à fort volume.

Garder Codemagic ou macOS hébergé GitHub ?

Comme secours archive ou overflow PR. Après self-hosting principal, le macOS hébergé convient si < 4 releases/mois ; équipes Flutter actives économisent souvent à long terme avec leurs runners.

Monorepo multi-apps — comment compter les jobs ?

Matrix × flavors se multiplient. Plus de quatre jobs macOS par PR : élargir le fast pool ou réduire la matrix — ne pas injecter l’archive dans les PR.

Navigation série (#9 Flutter bi-machine)
#1 capacité · #2 file d’attente · #3 coût self-hosted · #8 accélération build · #9 cet article

Deux Cloud Mac — CI Flutter Android et iOS bien séparées

Android reste sur Linux ; compile iOS, signature et export IPA exigent macOS. Deux Cloud Mac mini M4 avec macos-fast et macos-archive évitent que feedback PR et jobs release se disputent les slots — plus rentable qu’une archive dans chaque PR puis Queued pour tous. Apple Silicon fluidifie flutter build ios et le link Xcode ; ~4 W au repos conviennent au CI 7×24.

Face à une salle Mac de bureau : Cloud Mac par forfait, images reproductibles, lien GitHub stable — idéal pour équipes Flutter distribuées en France et en UE. Environnement Unix natif pour Flutter, CocoaPods, Fastlane sans couche de virtualisation.

Pour migrer la CI iOS de votre équipe Flutter : démarrez avec deux VPSSpark Cloud Mac mini M4 comme topologie minimalevoir les offres, PoC fast pool, puis pool archive pour les releases.

Offre limitée

CI Flutter bloquée sur iOS ? Deux Cloud Mac, deux pools

macos-fast + macos-archive · PR et release séparés

Accueil
Offre limitée Voir les offres