DevOps & audit 27 avril 2026

2026-04-27 Builds multiples Xcode.app, xcode-select et DEVELOPER_DIR par job dans une matrice CI sur Mac cloud loué (HK / JP / KR / SG / US)

Équipe technique MacXCode 27 avril 2026 ~20 min de lecture

Les équipes release et plateforme qui louent un seul Mac mini M4 Apple Silicon à Singapour pour App Store tout en gardant une voie rapide pour des branches expérimentales installent souvent plus d un Xcode.app. Le scénario d échec est prévisible : un job vert à 10h00 utilise Xcode 16.3 parce que quelqu un a exécuté xcode-select -s pendant un shell de support, un archive à 10h15 embarque silencieusement 16.2, puis App Store Connect rejette un binaire pour plancher SDK futur manquant. Ce runbook du 2026-04-27 est la couche opérationnelle au-dessus du conseil générique « épinglez votre toolchain » : quand préférer xcode-select, quand s appuyer uniquement sur DEVELOPER_DIR, comment nommer les bundles sous /Applications, et comment lier les labels de runners self-hosted à des chemins explicites pour ne plus débattre si clang provenait du bon répertoire Developer. À lire avec OpenClaw, quotas LLM 429/503 et budgets de retry si la même machine héberge des agents. Contexte : Swift 6 concurrence stricte CI, couverture et portes xcresult, DerivedData par job.

Pourquoi un second Xcode sur un hôte loué est normal en 2026

Le rythme des sorties Apple ne suit plus celui de vos sprints. Une ligne produit sur iOS 18 avec gestion prudente du risque peut rester sur une série stable 16.2 tandis qu une autre équipe valide 16.3 pour satisfaire des SDK minimum à venir avant de basculer la politique globale. Les nœuds physiques coûtent cher : un Mac cloud partagé à Tokyo ou Séoul porte les deux pendant des semaines, non pour entretenir deux checkouts .xcworkspace séparés. La bonne abstraction : un bail matériel, plusieurs toolchains logiques, avec sélection explicite et journalisée par job CI. Sans cela, votre « multi-matrice » YAML est du théâtre.

  • Fenêtres de conformité — Les exigences SDK minimum / build Xcode publiées par Apple avancent par dates. Un second toolchain sert parfois uniquement à prouver un candidat release en parallèle pendant que la prod reste épinglée.
  • Split test vs ship — Les branches d intégration compilent contre un SDK plus récent pour surfacer les deprecations, tandis que la ligne App Store garde un chemin codesign + export documenté avec export + API App Store Connect.
  • Économie de ferme — Doubler les nœuds loués pour chaque bump mineur de Xcode se justifie rarement ; un modèle DEVELOPER_DIR discipliné conserve NVMe et mémoire unifiée dans une enveloppe 24–32 Go si la planification est soignée.

Les équipes d exploitation doivent pouvoir expliquer sans ticket quel bundle a servi pour quel job ; sinon les revues sécurité réagissent au disque plein en supprimant des runtimes, puis un mois plus tard la reproductibilité disparaît.

xcode-select ou DEVELOPER_DIR : tableau de décision à quatre colonnes

Les deux mécanismes pointent la toolchain vers Xcode.app/Contents/Developer, mais le rayon d explosion diffère. Traitez xcode-select -switch comme un confort humain et DEVELOPER_DIR=... comme un contrat par processus en automatisation.

Contrôle Rayon Usage typique Risque à éviter
xcode-select -s /Applications/... Toute la session utilisateur et certains outils GUI au prochain lancement Debug SSH ad hoc, VNC + archive manuelle Xcode.app Deux jobs CI concurrents sur le même user : le dernier switch gagne ; course sur les symlinks Developer.
Export DEVELOPER_DIR=…/Xcode-16.2.app/Contents/Developer Processus enfants de ce shell / plist uniquement GitHub Actions, Buildkite, cron Oublier l export dans launchd et hériter d un PATH obsolète d une session login.
Chemins absolus (…/usr/bin/xcodebuild) Invocations uniques, explicites Diagnostics quand l héritage d environnement n est pas fiable Verbeux et fragile aux upgrades — après validation préférez DEVELOPER_DIR + xcodebuild simple.
Politique : les jobs CI sur hôtes partagés ne doivent jamais appeler sudo xcode-select en ligne. Si l orchestration exige vraiment une valeur par défaut globale (rare sur M4 mono-locataire), faites-le en fenêtre de maintenance avec ticket et xcodebuild -version journalisé après.

Disposition disque : rendre la toolchain active évidente dans ls /Applications

Règles de nommage qui survivent aux upgrades

Renommez le Xcode.app téléchargé en Xcode-16-2-0.app et Xcode-16-3-0.app dès l expansion du .xip, pas un mois plus tard de mémoire. Espaces et apostrophes dans le nom du bundle sont légaux mais pénibles en shell ; des fragments semver séparés par tirets correspondent à la façon dont vous taperez DEVELOPER_DIR sur clavier mobile en SSH. Gardez le suffixe .app : ne symlinkiez pas seulement le sous-arbre Developer sans paire Plist car les valeurs DTXcode alimentent les dSYM et l attribution crash.

du -sh /Applications/Xcode-*.app est votre signal capacité : un Xcode moderne complet fait souvent 12–20 Go avant simulateurs, et un second dossier plateforme majeur iOS ajoute 8–20 Go chacun. Sur des pools 1 To, utilisez le nettoyage disque CI pour supprimer des runtimes périmés seulement après 10 jours ouvrés à zéro sur une ligne de matrice — un chiffre défendable auprès de la sécurité.

Ne supprimez pas la toolchain la plus ancienne dès la première alerte stockage : croisez tickets ouverts, calendrier release et labels runner avant toute décision irréversible.

Matrice CI pilotée par labels : un runner, deux valeurs DEVELOPER_DIR

Que vous utilisiez des labels GitHub Actions self-hosted, des files Buildkite ou un équivalent nomad interne, l invariant est le même : un label de file (ex. xcode-16-2 vs xcode-16-3) doit correspondre à un unique ensemble d environnement exporté avant le démarrage de xcodebuild. Un tableau de mapping concret dans le README empêche un release manager d essayer « 16-3 en prod » sur le mauvais nœud.

Label Cible DEVELOPER_DIR Consommateurs prévus
xcode-stable /Applications/Xcode-16-2-0.app/Contents/Developer App Store, TestFlight, hotfix, LTS longue durée
xcode-next /Applications/Xcode-16-3-0.app/Contents/Developer Dep-Warn, pilote Swift 6, expériences helpers notarisés

Sous macOS, l héritage d environnement des runners self-hosted diffère entre runsvc.sh et les sessions ssh interactives. Injectez DEVELOPER_DIR dans le wrapper exact exécuté par le runner (ex. shim bash -lc "export …; exec $@"), pas dans votre ~/.zshrc personnel — sinon les invites keychain et signature réapparaissent mystérieusement pour une seule voie.

Runbook en huit étapes : installer, épingler, vérifier, avancer

  1. Décompresser le .xip vers /Applications avec un nom de bundle unique et explicite. Enregistrez shasum -a 256 du .xip dans le ticket de changement.
  2. Premier lancement (licence) via sudo xcodebuild -license accept en automatisation si la politique le permet, pour éviter les blocages sur les sous-commandes license.
  3. Mettre en cache les plateformes minimales pour ce bundle avec xcodebuild -download… pour les runtimes iOS/tvOS réellement utilisés par la matrice — pas un clic « tout installer » dans la GUI.
  4. Écrire un script court /usr/local/bin/mxcode-16-2 qui exporte seulement DEVELOPER_DIR et imprime xcodebuild -version sur stdout en lignes JSON-friendly ; commitez-le dans le dépôt infra.
  5. Mapper les labels de l orchestrateur vers ce script, puis relancez un job de test simulateur golden ; comparez Build version aux baselines.
  6. Réconcilier la signature : le chemin vers codesign change avec DEVELOPER_DIR — relancez les contrôles optimisation signature pour vérifier que l identité distribution est le même SHA-1 trousseau, pas un doublon avec une autre date not-valid-before.
  7. Provenance pour uploads TestFlight : écrivez Build version, ProductVersion et drapeaux vendeur CLANG dans un petit ci-toolchain.txt à côté de l IPA, comme pour les artefacts couverture + junit.
  8. Retirer un toolchain seulement quand 0 ticket ouvert référence le label — la pression disque est un runbook séparé ; planifiez le nettoyage via l article disque, pas un rm d urgence à 3 h.

export DEVELOPER_DIR="/Applications/Xcode-16-2-0.app/Contents/Developer" /usr/bin/xcodebuild -version

Pièges, audits et défense App Store

Trois signaux numériques à journaliser sur chaque job de compilation lorsque plusieurs Xcode coexistent : (1) le tuple complet xcodebuild -version incluant Build version, (2) echo $DEVELOPER_DIR après filtrage env, (3) un hash de votre plist exportOptions. Les auditeurs peuvent reconstruire pourquoi les builds 5421 et 5422 ont des UUID dSYM différents. Si vous notarisez des helpers macOS, vérifiez que le run notarytool utilise le même DEVELOPER_DIR que la compilation — mélanger les métadonnées codesign 16-2 et 16-3 sur un bundle ouvre un ticket long.

Dérive simulateur : la chaîne littérale -destination du type iPhone 16, OS=18.2 dans les tests headless doit être re-baselinée après chaque ajout/suppression de runtime ; sinon deux installs Xcode ne s accordent pas sur la carte nom vers UDID.

FAQ : multi-toolchain sur Apple Silicon loué

Question Réponse pratique 2026
Puis-je partager un DerivedData entre les deux toolchains ? Non en production : magasins d index et interfaces Swift diffèrent ; gardez l isolation de l article DerivedData et doublez la racine JOB_ID par job.
Le xcode-select global est-il sûr si l hôte est « seulement CI » ? « Seulement CI » est rare : les humains SSH encore pour repro. Préférez DEVELOPER_DIR en automatisation et documentez un switch de confort dans l aide pour l astreinte.

Pourquoi le Mac mini M4 nu à HK / JP / KR / SG / US colle encore à ce modèle

Épingler deux familles Xcode complètes est un schéma lourd en mémoire et E/S : le compilateur Swift, l indexation et les caches Metal shaders disputent le même pool de mémoire unifiée. Les fermes virtualisées ou Intel anciennes cachent souvent un second toolchain derrière un hyperviseur qui ment sur la localité NUMA ; un Mac mini M4 loué à Hong Kong, Tokyo, Séoul, Singapour ou aux États-Unis rend le calcul DEVELOPER_DIR honnête — ce que vous voyez dans clang -v est ce qui tourne sur le bare metal sous votre session SSH ou VNC. Si votre file est saturée, ajoutez un nœud in-region via les tarifs MacXCode avant de pousser la parallélisation sur un seul nœud 24 Go et d inviter des builds rouges « fantômes » qui sont en réalité de la pression mémoire. Si OpenClaw sollicite en parallèle des APIs LLM, croisez avec 429/503 et budgets de retry pour séparer les symptômes réseau des régressions build.

Placez la seconde toolchain là où vivent vos testeurs

1–2 To · Apple Silicon M4 · SSH et VNC optionnel