Vous êtes d'astreinte avec au moins deux Mac cloud loués et Fastlane pour iOS, et vous voyez des logs de compilation verts sans nouveau build TestFlight (ou le mauvais paquet). Pas encore décidé pour le cloud ou un second Mac ? Lisez d'abord le Mac dédié TestFlight ou déporter les builds Xcode — ici on suppose déjà une machine compile, une autre envoie.
À la fin, vous devriez savoir : ① si les logs pointent vers certificat, artefact ou réseau ; ② si les rôles des deux Mac sont corrects et s'il faut ajouter du matériel.
- Machine de build : compile dans Xcode et exporte uniquement le paquet d'installation (
.ipa) - Machine d'upload : signe avec le certificat de distribution et envoie le
.ipavers TestFlight ; l'IP de sortie enregistrée chez Apple doit être celle de cet hôte seul
Vous pouvez passer si : vous n'avez qu'un Mac, ou TestFlight fonctionne et vous n'avez pas encore séparé « build + upload ». Si plusieurs Mac cloud et pipeline en panne : continuez.
- Frontière certificat — le certificat de release a été synchronisé sur la machine de build, la machine d'upload ne peut pas signer (le plus grave)
- Frontière artefact —
.xcarchivetransféré entre DC au lieu d’une.ipavérifiable (la plus discrète) - Dérive d’identité réseau — whitelist ASC encore sur l’ancienne IPv4 upload (la plus pénible)
Contexte (1 min) : avec deux Mac, ce qui casse, ce sont les frontières
Beaucoup d'équipes louent deux Mac cloud : l'un près de l'Asie compile l'app en paquet le jour ; l'autre au Canada remet le paquet à Apple (TestFlight / App Store). Le second Mac vise la vitesse — mais l'astreinte souffre quand les deux touchent aux certificats et aux fichiers : build OK, TestFlight sans nouvelle version.
Découpe recommandée : modèle A (build = paquet, upload = publication)
En une phrase : la machine de build ne produit que l'.ipa ; seule la machine d'upload gère certificats et TestFlight. Ne lancez pas match sur les deux.
| Role | Machine de build (Hong Kong) Example: APAC daytime builds | Machine d'upload (Canada) Fixed IP for Apple |
|---|---|---|
| Do | ||
| Do not | ||
| Hand off | Package file + build ID (RUN_ID) | None (endpoint is App Store Connect) |
Les deux runners sont des Mac mini Hashvps dédiés — pour IP fixe + isolation des rôles (IP upload dans ASC, pas l’IP build). Spécifications Hong Kong : location Mac mini Hong Kong. Verrouiller l’identité upload ASC avant d’ajouter des runners build.
Les deux runners sont des Mac mini Hashvps dédiés — pour IP fixe + isolation des rôles (IP upload dans ASC, pas l’IP build). Spécifications Hong Kong : location Mac mini Hong Kong. Verrouiller l’identité upload ASC avant d’ajouter des runners build.
Erreur fréquente : modèle B (les deux « aident »)
Le modèle B, c'est des rôles non séparés — notre première semaine. En surface deux machines, en pratique :
- Les deux exécutent
match→ clé privée de release sur le build → l'upload ne signe pas → TestFlight en échec - Build rsync
.xcarchive→ second export upload, SHA différent → mauvais artefact, Invalid Binary - Double
export_ipa→ deuxExportOptions.plistqui dérivent → upload OK, build non promouvable
Si une ligne correspond à votre workflow, un troisième Mac n’aide pas — il faut le role split build/upload (modèle A), pas plus de runners.
Observabilité : prouver le même build des deux côtés
L'incident le plus courant : « on a uploadé l'IPA d'hier ». Les mêmes champs dans les logs build et upload :
RUN_ID(ex.hk-build-88421)GIT_SHA(commit court)- chemin artefact + SHA256 (
build/manifest.json)
# Hong Kong · build · build [15:11:42]: Successfully exported: ./build/MyApp-20260604-1502.ipa [15:11:42]: RUN_ID=hk-build-88421 GIT_SHA=a3f91c2 SHA256=9f2a… # Canada · upload · upload [02:22:01]: RUN_ID=hk-build-88421 GIT_SHA=a3f91c2 ipa_sha256=9f2a… [02:24:18]: Successfully uploaded package to App Store Connect
Règle d’astreinte : RUN_ID upload ≠ build → traiter comme erreur d’artefact avant ASC.
Triage 30 s : pas de nouveau build TestFlight ?
Ne pas lire tout le log. Une branche de l’arbre suffit pour ouvrir le bon chapitre (à bookmarker dans le runbook) :
Pas de nouveau build TestFlight ?
├─ RUN_ID upload ≠ build (ou pas de manifest)
│ → piège 2 · frontière artefact
├─ Log : match / AppStoreDistribution / code signing identity
│ → piège 1 · frontière certificats
└─ Upload « réussi », ASC vide / 403 / Invalid Binary (build tout vert)
→ piège 3 · dérive d’identité réseau
| Mode | Mots-clés log (grep) | Impact release |
|---|---|---|
| Piège 1 certificats | AppStoreDistribution, No matching provisioning, match failed |
Bloquant : TestFlight / Store arrêtés |
| Piège 2 artefact | RUN_ID incohérent, Invalid Binary, conflit export_method |
Élevé : mauvais build ou pas de promote |
| Piège 3 identité | 403, upload OK sans processing, limites réseau ASC |
Intermittent : effet « hasard » |
Trois modes d’échec (détail)
Piège 1 : mauvaise frontière certificats
30 s : archive build verte ; upload rouge sur match / signing.
Log : AppStoreDistribution, Could not find a matching code signing identity, No matching provisioning profiles.
Release : bloquant — TestFlight et App Store jusqu’à ce que la clé privée ne soit que sur upload.
Symptôme : archive OK côté build, lane upload en échec ; TestFlight sans nouveau build.
Could not find a matching code signing identity for type 'AppStoreDistribution' ❌ Lane upload failed
Cause : modèle B — le build exécute encore match(appstore), clé Distribution absente de l’upload.
Correctif : retirer tout match du build ; upload exclusif (rotate uniquement là, fenêtre dédiée). Voir match readonly.
Piège 2 : mauvaise frontière artefact
30 s : comparer RUN_ID et ipa_sha256 build vs. upload ; ou ASC Invalid Binary sans erreur signing sur le build.
Log : RUN_ID différent, Invalid Binary, double export_ipa / ExportOptions.
Release : élevé — ancien commit, mauvais build ou processing bloqué ; pipeline peut rester verte.
Cause : .xcarchive entre DC ou double export. Unité minimale inter-DC : IPA avec SHA, pas le dossier archive.
Correctif : un seul export_ipa + manifest.json sur le build ; upload = upload_to_testflight seulement.
Piège 3 : dérive d’identité réseau
30 s : Fastlane annonce un upload réussi ; signing vert partout ; TestFlight vide ou ASC 403.
Log : 403, Successfully uploaded sans build, refus réseau ASC malgré logs build verts.
Release : intermittent — parfois ça passe, parfois non.
Cause : IPv4 upload ≠ whitelist ASC (troisième classe silencieuse).
Correctif : quotidien sur upload : curl -4 ifconfig.me vs. ASC ; drift → whitelist + alerte.
Comment savoir que le split est réel
Checklist fusionnée — une semaine après le déploiement, chaque ligne doit être « oui » sans débat :
| Contrôle | Attendu |
|---|---|
| RUN_ID aligné | Logs export build et upload upload : RUN_ID, GIT_SHA, ipa_sha256 identiques |
| Build sans Distribution | Sur le build, trousseau/logs jamais AppStoreDistribution ni step match |
| Upload = distribution seule | Lane upload : uniquement match (ou resign) + upload_to_testflight, pas ARCHIVE SUCCEEDED |
| Forme artefact | Entre DC : .ipa + manifest (< 200 Mo) ; pas de rsync .xcarchive |
| Identité ASC | IPv4 upload courante = whitelist ASC ; IP build absente d’ASC |
| Isolation runners | Labels role=ios-build-hk et role=ios-upload-ca, chaîne needs |
Stade d’évolution : où en êtes-vous ?
L’incident répond à « cette nuit » ; le stade à « la semaine prochaine, changer l’architecture ? ». Quatre stades — maintenir, corriger ou imposer le role split. Beaucoup passent du stade 1 à deux Mac sans labels et atterrissent au stade 2 ; ce texte mène au stade 3 avec acceptation mesurable.
Stade 1 : un Mac, une Fastlane
Signes : un runner ; match, build, upload dans le même Fastfile. Optimiser disque, Derived Data et facturation GitHub Actions avant un second nœud build.
Conclusion : pas besoin de dual Mac. Lenteur APAC seule → ajouter un nœud build, ne pas scinder l’upload en premier.
Stade 2 : deux Macs sans role split (modèle B)
Signes : deux+ runners ; plusieurs exécutent match / export / upload ; logs avec les trois frontières de cet article. Souvent parce que « chaque Mac doit tout faire un peu » — c’est ce qui crée la dérive ; le stade 3 impose labels et chaîne needs clairs.
Conclusion : ❌ Un troisième Mac ne résout rien, augmente la dérive certificats/artefacts. Étape suivante : modèle A, pas plus de runners.
Stade 3 : role split (modèle A · cible)
Signes : build/upload séparés ; entre DC seulement IPA + RUN_ID ; match uniquement sur upload ; IP upload fixe.
Conclusion : ✔ checklist stable → scaler les runners build ou durcir l’upload. IP fixe et nœuds : offres Hashvps (verrouiller l’IP ASC upload avant d’étendre le build).
Stade 4 : plusieurs régions upload (avancé)
Signes : upload US/EU/APAC ; clés ASC et IP par région ; promotion et conformité en ligne dédiée.
Conclusion : plateforme de distribution, pas « un Mac CI de plus » — registry, politique, audit ; hors de ce runbook.
| Stade | Action prioritaire |
|---|---|
| Stage 1 | Garder un Mac ; optimiser créneaux build/upload |
| Stage 2 | Stopper les achats Mac ; modèle A + checklist |
| Stage 3 | Étendre le build ; monitoring upload |
| Stage 4 | Projet séparé : multi-région, pas patch Fastlane |
Verdict final (ce bloc suffit pour exécuter)
Si les trois conditions ci-dessous sont vraies, tranchez ici sans relire l’article :
- Au moins 2 Mac runners (ou CI multi-nœuds équivalent)
- Chaîne Fastlane avec TestFlight instable (
match failed, processing bloqué, builds absents) matcha tourné sur plus d’une machine
👉 N’achetez plus de machines.
👉 Trois actions (démarrables aujourd’hui) :
- Arrêter
match/sync_code_signinghors nœud upload - Forcer entre DC uniquement
.ipa+manifest.json(interdire rsync.xcarchive) - Unifier
RUN_ID+GIT_SHA+ipa_sha256avant upload
Sinon : vous êtes au stade 2 (modèle B). Plus de hardware agrandit la panne. Fastfile et labels workflow d’abord, checklist verte, puis capacité build.
lane :upload_only do |options| verify_run_id!(options) # manifest.json match(type: "appstore", readonly: true) upload_to_testflight(ipa: options[:ipa_path], api_key_path: "asc_api_key.json") end
Questions courtes
Le build peut-il archiver sans Distribution ? Selon le projet ; nous exportons une IPA pour signature App Store finale sur l’upload (ou build = compile check, upload resign). Règle : match sur un seul host.
match expire bientôt ? Rotate uniquement sur upload en fenêtre non-readonly ; pause build jusqu’à profils stables.
Différence avec le runbook voyage ? Voyage = Wi‑Fi sur site ; ici = rôles datacenter et frontières Fastlane — checklist copiable telle quelle.
Série : ① Runbook : dual cloud Mac + Fastlane (TestFlight vide / match failed / CI iOS multi-Mac). Suite : ② Build·Sign·Distribute, ③ anti-modèle B ; entrée setup TestFlight.