Vous utilisez déjà GitLab (SaaS ou auto-hébergé), mais les builds iOS/macOS restent coincés entre files de runners macOS hébergés, portables à double usage ou montée de Jenkins. Pour les ingénieurs plateforme qui pensent en exploitation VPS, ce guide ouvre la voie « louer un Mac comme un nœud » : sept listes de contrôle pour faire apparaître la friction réelle avec GitLab Runner sur un Mac distant dédié, un tableau comparatif à trois entrées face aux agents SSH Jenkins et aux runners auto-hébergés GitHub Actions, puis un runbook de passation en six étapes (enregistrement, tags, répertoires de cache, concurrence et esquisse de .gitlab-ci.yml), avec des liens vers nos articles runner, Jenkins et gouvernance disque SwiftPM/Pods.
La doc GitLab rend gitlab-runner register fluide, mais en production le temps part souvent à la machine à états de la toolchain Apple et au pietinement concurrent du disque. Utilisez les sept points ci-dessous pour transformer « ajoutons un runner » en tableau de risques signable.
Traiter le Mac distant comme un runner Linux : ignorer TCC, le trousseau et les besoins GUI occasionnels fait exploser la première signature — revoir avec la checklist SSH vs VNC.
S’enregistrer sous un compte macOS personnel : veille, invites de mise à jour et sessions bureau cassent les flux sans présence — utiliser un utilisateur CI dédié aligné sur les builds reproductibles.
Dimensionner la concurrence sur les cœurs seuls : les pics RAM Xcode et l’amplification NVMe mordent d’abord ; sans DerivedData segmenté, deux jobs peuvent se bloquer — même contrat que la gouvernance disque SwiftPM/Pods.
Négliger jetons et hygiène d’enregistrement : disperser des sauvegardes config.toml en clair crée une fausse confiance quand la rotation rend les files rouges.
Copier-coller des politiques de cache : de mauvaises clés cache: croisent les branches ou ratent toujours — concevoir des clés avec dimensions branche et fichier de verrouillage.
Ne laisser les artefacts que sur le disque du runner : sans artefacts GitLab ou stockage objet, disque et conformité souffrent — lier la rétention à la revue sécurité.
Pas de « première fenêtre humaine » : les profils de signature initiaux peuvent encore exiger une confirmation VNC ou bureau unique avant de repasser headless — voir Fastlane + CI.
La cause racine partagée : traiter le « Mac distant » comme du calcul brut plutôt qu’un hôte avec empreintes Xcode et frontières du trousseau. Maintenir empreintes d’image, versions de toolchain, seuils de nettoyage et contrats de tags comme des réplicas de base. Coupler avec les pools de build entreprise : quand plusieurs projets partagent un hôte, les tags GitLab doivent être plus fins que « n’importe quel Mac » ou resource_group n’exprime pas une vraie isolation.
Face à GitHub Actions, l’écart n’est pas « est-ce que ça compile » mais définition de pipeline et sources d’événements : .gitlab-ci.yml s’attache nativement aux cycles de vie MR ; Actions s’attache aux PR mais coûte cher à quitter GitHub. Si vous êtes 100 % GitLab, macOS en runner shell est souvent plus cohérent qu’un second dialecte Jenkins pour iOS — pourtant Jenkins gagne encore certaines revues d’orchestration on-prem. Le tableau suivant fige les compromis.
Avant d’enregistrer, lisez les sections cache et labels de notre guide runner : la plupart des contrats de répertoire se traduisent directement en cache GitLab et CI_PROJECT_DIR ; seul le déclencheur change de workflow à pipeline.
Il n’y a pas de solution universelle — vous choisissez un modèle mental d’orchestration et une frontière des identifiants. Inscrivez trois SLA dans la revue : latence de file, échecs explicables et coût de rotation des clés.
| Dimension | GitLab Runner (shell macOS) | GitHub Actions auto-hébergé | Agent SSH Jenkins |
|---|---|---|---|
| Définition du pipeline | .gitlab-ci.yml est natif aux projets/MR ; modèles et includes matures | YAML dans le repo fortement couplé aux événements PR/Issue | Job DSL / Pipeline Groovy — orchestration multi-dépôts flexible mais dérive de style plus forte |
| Modèle d’enregistrement | Jetons projet/groupe ; config.toml centralise exécuteur et tags | Jetons runner org/dépôt avec mise en place relativement standard | Le contrôleur tient les identifiants SSH — durcir le plan de contrôle |
| Concurrence et limitation | resource_group, parallel, limites de concurrence du runner | Matrices et concurrency dans le YAML | Labels + plugins de limitation — flexible mais lourd en configuration |
| Cache et artefacts | cache/artifacts natifs ; de mauvaises clés polluent les caches | Écosystème riche actions/cache et artefacts | Souvent bricolage vers stockages objet |
| Meilleur cas | Organisations centrées GitLab avec pipelines MR et pools unifiés | Centré GitHub, livraison pilotée par PR | Lignes de produits multiples, artefacts on-prem, validations, hôtes Git mixtes |
Louer un Mac « comme un VPS » en termes GitLab, c’est acheter un profil de runner enregistrable : SSH fixe, niveaux de disque prévisibles et capacité d’estampiller les empreintes Xcode dans les tags.
Si GitLab Runner l’emporte, traitez les tags en citoyens de première classe : minor Xcode, si un lourd pod install est autorisé, si les tests UI tournent — tout doit être explicite. Coupler avec snapshots vs nœuds longue durée : les runners longue durée s’appuient sur un nettoyage incrémental ; les images dorées sur des images réchauffées et des tests de fumée de rollback.
Si vous exploitez plusieurs systèmes CI, unifiez le partitionnement DerivedData pour que les jobs GitLab n’écrasent pas les jobs Jenkins ou Actions sur le même Mac distant — utilisateurs Unix ou racines séparés, pas « espérons que des horaires décalés suffisent ».
L’ordre compte : identité et répertoires d’abord, enregistrement et tags ensuite, concurrence en dernier — aligner les scripts d’empreinte avec les builds reproductibles pour que GitLab valide une signature stable, pas seulement « git clone marche ».
Créer un utilisateur dédié et une racine de travail : par ex. /Users/ci/gitlab-runner, jamais mélangée au ~/Desktop personnel ; SSH par clé uniquement.
Installer gitlab-runner : paquet macOS officiel ou Homebrew ; binaire dans le PATH et exécutable sous compte de service (launchd).
Lancer register : choisir l’exécuteur shell, fournir l’URL GitLab et le jeton d’enregistrement ; épingler tag_list (ex. ios,shell,m4) en interactif ou non interactif.
Premier job de contrôle santé : afficher xcode-select -p, xcodebuild -version, swift --version et instantanés disque ; conserver le journal comme preuve d’acceptation du runner.
Passer DerivedData explicitement dans .gitlab-ci.yml : même contrat que la gouvernance disque SwiftPM/Pods — segment par projet, éviter les chemins partagés par défaut.
Définir timeouts, artefacts et nettoyage : timeout, rétention en échec et arrêt quand le disque est bas (monitoring + API pour suspendre les runners).
stages: [build]
variables:
DERIVED_DATA: "$CI_PROJECT_DIR/.derivedData/$CI_PIPELINE_ID"
build_ios:
stage: build
tags: [ios, shell, m4]
timeout: 45m
script:
- xcode-select -p
- xcodebuild -version
- df -h
- xcodebuild -scheme "App" -configuration Release -destination "generic/platform=iOS" -derivedDataPath "$DERIVED_DATA" build
artifacts:
when: on_failure
paths:
- "**/*.xcresult"
expire_in: 3 days
Astuce : si les pipelines livrent aussi vers les stores, lire Fastlane + CI et aligner utilisateurs de build, partitions du trousseau et clés App Store Connect API avec les variables GitLab CI/CD (masquer + protéger les secrets).
Lors de mises à jour GitLab ou runner, lancer un job iOS canary sur le même commit avant/après et comparer sortie d’empreinte et distribution des temps de build. Face au cache runner : des clés GitLab trop lâches laissent la branche A empoisonner le cache Pods de B ; des clés trop strictes signifient des démarrages à froid sans fin — plateforme et produit doivent convenir des niveaux de rétention.
Si votre fournisseur impose des ports SSH fixes et des utilisateurs non root, centraliser les paramètres de connexion dans un runbook interne, pas dans des descriptions de variables éparpillées — faire tourner la rotation à un seul endroit. Coupler avec Jenkins + Mac distant : les baselines SSH (clés, pare-feu, audit) doivent être une seule source à travers les piles CI, pas trois dialectes.
resource_group et limitation des installations de dépendances lourdesUne erreur fréquente : dimensionner la concurrence par « combien de processus xcodebuild tiennent ». pod install / résolution SPM et pics de compilation surviennent souvent dans des phases différentes — exprimez des ressources mutex dans .gitlab-ci.yml avec resource_group ou scindez les jobs. Avec la gouvernance SwiftPM/Pods : limitez séparément les jobs de résolution lourds pour qu’ils ne volent pas les créneaux des builds verts fréquents.
Les tests sont une autre dimension cachée : les tests UI Simulateur demandent un autre modèle de concurrence que les pipelines compilation seule — lire le partitionnement XCTest et Simulateur et isoler avec tags dédiés ou pools de runners dans GitLab.
Attention : ne continuez pas à remplir la file quand le disque est sous les seuils sûrs — suspendez la planification et nettoyez d’abord, sinon vous risquez un état Xcode/git à moitié écrit plus coûteux qu’une courte attente en file.
Si vous avez des runners dans plusieurs régions, encodez la région dans les noms et tags et étiquetez les chemins d’artefacts pour que les gros transferts inter-régions ne soient pas lus comme des échecs de build. Coupler avec achat vs location TCO : latence et egress appartiennent au modèle de coût dès le départ.
Utilisez les puces ci-dessous pour l’alignement interne ; ajustez les seuils à la taille du dépôt et au parallélisme.
gitlab-runner --version, xcodebuild -version, Ruby/Bundler si CocoaPods, modèle de disque ; déclencher un pipeline canary après changement.Les Mac « bureau uniquement » subissent veille, jitter réseau et dérive de toolchain ; Linux ne peut pas héberger la toolchain iOS officielle d’Apple. Garder GitLab dans le flux web/MR familier tout en déplaçant l’exécution macOS vers des nœuds distants dédiés, toujours allumés, joignables en SSH transforme la « source unique de vérité pipeline » d’un slogan en contrat. Face au matériel possédé ad hoc ou aux piles Xcode virtualisées fragiles, la location cloud Mac Mini NodeMini est souvent le meilleur pari plateforme car les extrémités SSH, les niveaux disque et les profils de runner reproductibles sont plus clairs ; comparez fiches et tarifs dans les tarifs de location, puis finalisez l’onboarding avec le centre d’aide.
Liez ce runbook aux niveaux de changement toolchain internes : patch/minor/major Xcode mappent sur des approbations, une portée canary et des politiques d’invalidation de cache différentes.
La plupart des équipes iOS/macOS natives commencent par l’exécuteur shell pour la moindre friction avec Xcode, le trousseau et le simulateur. Docker convient aux stacks conteneurisées ou à une isolation plus forte, mais coûte plus sur les toolchains Apple. Comparez d’abord les niveaux matériel dans les tarifs de location.
Ne dimensionnez pas sur les cœurs seuls : mesurez la RAM de pic et l’amplification NVMe d’un job seul, puis surveillez le P95 en ajoutant de la concurrence ; segmentez DerivedData et les caches de dépendances et limitez les jobs pod install lourds. Pour l’onboarding, voir le centre d’aide.
GitLab associe les jetons runner projet/groupe à .gitlab-ci.yml ; Jenkins s’appuie sur l’orchestration d’entreprise et les plugins ; Actions est très lié aux événements PR. Documentez sources d’événements, frontières des secrets et SLA de file — pas les logos. Poursuivre avec Jenkins + Mac distant et runners GitHub Actions.