Quand la livraison se compresse en pics de quelques heures, GitLab CI reste souvent le système de référence pour les merge requests, les branches protégées et les preuves de release — alors que la voie coûteuse, c'est toujours Apple Silicon avec Xcode et le trousseau de signature. Enregistrer un gitlab-runner auto-hébergé sur un Mac cloud dédié avec l'exécuteur shell conserve Fastlane et xcodebuild sur un vrai macOS sans surcoût Docker-dans-VM ; les politiques de cache et les tags de runner décident ensuite si le trafic burst reste prévisible ou s'écrase dans une file unique. Ce mémo condense ce que nous figeons avant la charge : paramètres YAML actionnables, champs de clé de cache, dimensions d'étiquettes, et comment éviter de doubler les secrets quand le même dépôt vit aussi sur GitHub Actions. Pour l'enregistrement réseau et les jetons minimalistes après activation d'un Mac cloud, voir aussi 2026, pics CI à cycle court : après activation du Mac cloud — enregistrer le runner, auto-contrôle réseau et jetons minimalistes (checklist 30–60 min + FAQ).
Exécuteur shell sur Mac cloud : pourquoi, et quoi casse en premier
L'exécuteur shell réutilise la session macOS de l'utilisateur qui a lancé gitlab-runner run : Fastlane et xcodebuild voient le même trousseau et les mêmes chemins que lors d'une validation SSH. C'est la fidélité recherchée ; la contrepartie est l'isolation limitée. Préférez un utilisateur CI non admin, épinglez xcode-select, et placez les caches Homebrew et CocoaPods sous ce home pour raccourcir les préambules. Pour le dimensionnement des pools macOS et le débat élastique versus toujours actif côté GitHub dans un setup hybride, croisez avec 2026, pics CI à court cycle : runners GitHub Actions macOS auto-hébergés — pool élastique de Mac cloud ou nœuds permanents ? : le calcul de file d'attente reste le même même quand GitLab porte le graphe de pipeline.
security find-identity -v -p codesigning, et exclure les hôtes dont le profil MDM fait tourner les jetons en plein sprint.
Clés de cache que GitLab peut raisonner sous rafale de merges
Les restaurations GitLab sont conservatrices : une clé trop étroite déclenche des fetchs en meute ; une clé trop large empoisonne les builds incrémentaux après modification des lockfiles. Nous ancrons sur $CI_COMMIT_REF_SLUG seulement quand une branche partage réellement des artefacts ; pour les voies Xcode sur branche par défaut, préférez $CI_JOB_NAME plus une somme de contrôle sur Gemfile.lock, Podfile.lock et le graphe SwiftPM résolu quand il est disponible. Ajoutez $CI_RUNNER_EXECUTABLE_ARCH (ou une variable dérivée de uname -m dans before_script) pour que Rosetta et les binaires natifs ne partagent pas les mêmes restaurations Ruby ou Pods. Pour DerivedData sur shell, nous synchronisons souvent depuis un répertoire chaud sous le home CI et n'emballons ce chemin dans le cache GitLab que si la version du runner le supporte proprement.
.gitlab-ci.yml — bloc cache (illustratif)
cache: key: files: - Gemfile.lock - Podfile.lock prefix: "${CI_JOB_NAME}-${CI_RUNNER_EXECUTABLE_ARCH}" paths: - .bundle/ - Pods/
Tags des runners : router Xcode, tenir les scans hors du pool Mac
Les tags doivent encoder ce que l'ordonnanceur n'infère pas : macos-15, xcode-16.2, cloud-pool-a. Les fenêtres burst reçoivent une paire d'étiquettes dédiée pour que les jobs release ne rivalisent pas avec des pipelines exploratoires. Associez concurrent dans config.toml au nombre de cœurs physiques moins une marge pour les tests UI sans affichage. Si vous opérez en parallèle des agents Jenkins sur des hôtes proches, la topologie contrôleur léger + JNLP documentée dans l'article Jenkins du journal reste un bon repère pour vider les files sans affamer GitLab.
config.toml — esquisse runner shell
[[runners]] name = "cloud-mac-gitlab-shell" url = "https://gitlab.example.com/" token = "__REDACT__" executor = "shell" shell = "bash" [runners.custom_build_dir] enabled = true
Mélange avec GitHub Actions : matrice et paramètres exécutables
Des équipes miroirent le dépôt vers GitHub pour les workflows communautaires tout en gardant GitLab pour la facturation et la conformité. Gardez un jeton d'écriture par système, des rotations de calendrier différents, et interdisez qu'un même PAT personnel couvre les deux ordonnanceurs. GitHub peut porter le fan-out Linux (os: ubuntu-latest) et GitLab les voies macOS protégées, ou l'inverse si GitHub possède déjà la signature — mais évitez de dupliquer archive-et-upload sans aligner bundle IDs et identités de notarisation.
| Objectif | Leviers GitLab | Leviers GitHub |
|---|---|---|
| Réduire la file p95 macOS | Augmenter concurrent, ajouter des runners tagués, scinder les clés de cache par mineur Xcode |
runs-on: [self-hosted, macOS, xcode-16.2] et max-parallel plus large si sûr |
| Réduire le rayon des secrets | CI_JOB_TOKEN à périmètre projet, variables d'environnement, OIDC si disponible | Règles de protection d'environnement, OIDC cloud vers AWS/GCP plutôt que clés longue durée |
| Une seule source de scripts | Makefile partagé dans before_script ; hacher le script vendeur dans la clé de cache |
Workflow réutilisable invoqué depuis les deux plateformes via une fine enveloppe |
workflow_dispatch manuel côté GitHub et des rules: if: $CI_PIPELINE_SOURCE == "merge_request_event" côté GitLab pour ne builder l'archive qu'une fois. Journalisez l'ID du runner dans les artefacts pour prouver quel hôte a produit le binaire.
FAQ : paramètres que nous copions dans les runbooks
Tags minimum pour une voie de signature ?
Utilisez tags: [macos, xcode-16.2, signing] et réservez signing aux hôtes où les certificats de distribution sont installés ; ne partagez pas ce tag avec les runners « lint only ».
Quand désactiver le cache sur une branche hotfix ?
Mettez cache: {} sur le modèle hotfix unique ou surchargez avec cache.policy: pull pour qu'une archive empoisonnée ne se propage pas avant suppression dans l'admin GitLab.
Mises à jour du runner en pleine crunch ?
Gelez les mineurs ; planifiez gitlab-runner upgrade seulement après des builds verts sur un tag canary du type xcode-next.
Avant une semaine de charge, nous vérifions en une passe : étiquettes signing strictement disjointes des runners d'analyse statique, préfixe de clé de cache incluant l'ordonnanceur (gl- / gha-) pour éviter toute collision silencieuse entre archives, et journalisation du CI_RUNNER_ID sur les jobs qui poussent une build release. Si la file p95 macOS dépasse encore le budget produit après ces réglages, la réponse est presque toujours capacité ou découpage de graphe — pas un paramètre YAML magique caché.
Exécuter ces voies GitLab sur un Mac mini cloud stable
Les exécuteurs shell récompensent un matériel qui ne surprend pas : Apple Silicon offre une bande passante mémoire prévisible pour le linker et le compilateur Swift, macOS tient des semaines sans le rythme de redémarrage fréquent sur certaines fermes Windows, et Gatekeeper plus SIP réduisent les chaînes d'outils altérées sur le chemin de signature. Un nœud classe Mac mini à environ 4W en idle se laisse connecter pour des fenêtres burst plus facilement qu'une tour qui consomme encore hors charge.
L'écosystème Unix natif, Homebrew et les flux SSH-first permettent au même hôte d'héberger GitLab Runner le jour et des diagnostics ad hoc la nuit sans réimager — ce qui abaisse le coût total face à des VM éphémères pour chaque hotfix.
Si vous dimensionnez une capacité macOS dédiée à GitLab et voulez des performances Apple Silicon sans acheter le métal tout de suite, le Mac mini M4 cloud VPSSpark est un point de départ pragmatique — découvrir les forfaits et garder vos builds burst sur du matériel conforme à ce qu'attend Xcode.