Un Runner Mac affiché « Idle » ne garantit pas qu’un Job puisse être exécuté : GitHub Actions évalue les labels, l’accès au Runner Group, l’état réel du nœud et les règles de concurrence. La documentation GitHub précise qu’un Job destiné à un Runner auto-hébergé doit correspondre aux labels demandés et être autorisé à utiliser le groupe concerné (routage officiel des Runners auto-hébergés).

La conclusion opérationnelle est donc simple : ne commencez pas par ajouter un Mac. Dans cet ordre, vérifiez d’abord l’absence de Runner admissible, puis l’occupation de tous les nœuds compatibles, puis le service macOS qui ne reçoit plus correctement les tâches. Ce n’est qu’après la correction du routage ou du service, si une file stable subsiste, qu’une capacité distante supplémentaire ou un découpage des pools devient défendable.

Cet article s’adresse aux développeurs mobiles qui utilisent GitHub Actions pour des compilations Xcode et voient leurs Jobs rester en attente. Il concerne aussi les ingénieurs DevOps responsables des labels, du Runner Group, des droits de dépôt et du service permanent sur macOS, ainsi que les responsables de plateforme qui doivent choisir entre réparer, réenregistrer ou étendre un nœud.

01

Le premier diagnostic doit expliquer l’état « queued »

Avant toute modification, conservez une photographie exploitable de la panne. Enregistrez l’identifiant du workflow, le nom du Job, le contenu de runs-on, les labels visibles du Runner, son groupe, son état dans l’interface et les dernières lignes du journal de diagnostic. Cette précaution évite de perdre la preuve initiale après une modification de YAML ou une réinscription du nœud.

Un Job en attente peut correspondre à plusieurs mécanismes qui se ressemblent dans l’interface :

  • une condition if ou un Job précédent n’est pas encore satisfait ;
  • une validation manuelle ou une règle d’environnement attend une approbation ;
  • une règle de concurrence conserve le Job en attente ;
  • aucun Runner auto-hébergé ne satisfait simultanément les labels et les droits ;
  • les Runners admissibles sont occupés ;
  • le Runner est visible mais son service local n’est plus capable de récupérer une tâche.

La concurrence mérite une vérification séparée. Dans une même clé de concurrence, GitHub documente un maximum d’un élément en cours d’exécution et d’un élément en attente ; une nouvelle exécution peut donc remplacer l’élément en attente selon la configuration d’annulation (documentation officielle sur la concurrence GitHub Actions). Une file observée ne prouve donc pas automatiquement qu’il manque un Mac.

Relevez également le premier message concret affiché dans le détail du Job. Un commentaire indiquant qu’aucun Runner ne correspond n’a pas la même valeur qu’un Job retenu par une règle d’environnement. Il faut d’abord classer la panne avant de changer la machine.

02

Première étape : prouver la correspondance complète des labels

Le champ runs-on n’est pas une préférence : il définit les contraintes de sélection du Runner. Lorsque plusieurs labels sont indiqués, le même nœud doit les posséder tous. La documentation GitHub décrit notamment les labels liés au système, à l’architecture et au type self-hosted (guide officiel sur les labels des Runners auto-hébergés).

Un exemple courant peut ressembler à ceci :

jobs:
  build:
    runs-on: [self-hosted, macOS, ARM64, xcode-ci]
    steps:
      - uses: actions/checkout@v4
      - run: xcodebuild -version

La vérification doit comparer ce tableau logique avec les labels réellement enregistrés, et non avec les labels que l’équipe pense avoir appliqués. Les erreurs fréquentes sont les suivantes :

  • macos dans le workflow alors que le Runner porte macOS ;
  • arm64 demandé alors que le label effectif est ARM64 ;
  • label personnalisé supprimé lors d’une reconstruction ;
  • label d’outil conservé dans le YAML alors que Xcode ou un script associé n’est plus présent ;
  • ancien label laissé sur un nœud, donnant une impression de compatibilité alors que l’environnement a changé.

Pour isoler le routage, créez temporairement un Job sans secret ni publication :

name: diagnostic-runner

on:
  workflow_dispatch:

jobs:
  route-check:
    runs-on: [self-hosted, macOS, ARM64]
    steps:
      - name: Afficher le nœud sélectionné
        run: |
          echo "Runner: $RUNNER_NAME"
          echo "Système: $RUNNER_OS"
          echo "Architecture: $RUNNER_ARCH"
          sw_vers

Le résultat attendu est une exécution complète sur le nœud visé. Si ce Job reste en attente, l’échec se situe avant Xcode : labels, groupe, droits ou disponibilité. S’il démarre, le problème se déplace vers le label personnalisé, le pool de production ou l’étape de construction.

Ne remplacez pas durablement les labels précis par self-hosted uniquement pour faire disparaître la file. Cette modification peut envoyer une compilation vers un Mac sans le bon SDK, sans le bon outil de signature ou sans l’architecture attendue. Le diagnostic doit réduire les contraintes pendant quelques minutes, puis restaurer la sélection stricte.

03

Deuxième étape : distinguer les labels des droits du Runner Group

Un label correctement renseigné ne donne pas automatiquement accès au Runner. Le groupe auquel appartient le nœud applique une autre couche de décision : le dépôt, l’organisation ou le périmètre autorisé doit pouvoir utiliser ce groupe. GitHub sépare explicitement la gestion des groupes et leur accès aux dépôts (documentation officielle sur l’accès aux Runner Groups).

Cette distinction explique un cas trompeur : le Runner est en ligne, porte exactement les labels du Job, mais GitHub ne le sélectionne jamais pour le dépôt concerné. Dans cette situation, il faut relever :

  • le nom exact du Runner Group ;
  • le niveau auquel le groupe est défini ;
  • la liste des dépôts autorisés ;
  • l’organisation propriétaire du dépôt ;
  • la présence éventuelle d’une restriction héritée ;
  • la différence entre le groupe observé dans l’interface et celui attendu par l’équipe.

Effectuez ensuite un test avec une branche ou un dépôt de diagnostic qui ne contient ni secret de signature ni étape de publication. Le but est de vérifier que le dépôt voit réellement le groupe, pas de contourner la politique d’accès. Enregistrez la liste des Runners visibles avant et après la correction ; cette différence constitue une preuve plus solide qu’un simple changement de statut.

Une modification de permission peut élargir l’accès à des machines contenant des certificats, des caches ou des fichiers de travail sensibles. Avant de l’appliquer, notez la configuration précédente et le responsable capable de la restaurer. Ne supprimez pas le groupe et ne réenregistrez pas le Runner pour traiter un problème d’autorisation : ces actions détruisent la piste de diagnostic sans réparer la frontière d’accès.

04

Troisième étape : déterminer si les nœuds admissibles sont réellement occupés

Lorsque les labels et le Runner Group sont corrects, vérifiez l’occupation réelle. Un nœud peut être marqué Busy à cause d’une compilation Xcode active, d’un test Simulator qui ne se termine pas, d’une étape de signature bloquée ou d’un script long qui ne restitue pas rapidement le contrôle. À l’inverse, un processus terminé peut laisser un état local incohérent et faire croire que la capacité est utilisée.

La comparaison doit porter sur trois sources :

Indice observé Preuve à collecter Décision prudente
Job actif dans GitHub Actions Étape en cours, dernier journal et horodatage de l’activité Laisser le nœud occupé si le processus progresse
Runner occupé sans progression Processus Xcode, Simulator, archive ou script encore présent Identifier le processus bloqué avant tout redémarrage
Runner affiché Idle mais aucun Job reçu Journaux Runner, service launchd, connexion et répertoire de travail Traiter la couche service, pas la capacité
Tous les nœuds compatibles exécutent des Jobs légitimes Répartition des tâches et durée des étapes Envisager un autre pool ou une capacité additionnelle
Aucun nœud ne reçoit le Job minimal Résultat du test de routage et droits du groupe Revenir aux labels et aux autorisations

Une compilation Xcode et un test d’interface Simulator ne mobilisent pas toujours le nœud de la même manière. Un pipeline peut aussi monopoliser une machine parce qu’il conserve un simulateur, un processus de signature ou un serveur local. Pour cette raison, ne concluez pas à une pénurie sur la seule présence d’une file.

Examinez le Job actif, son dernier événement et les processus locaux avant de tuer une tâche. La suppression brutale peut laisser des verrous, interrompre une archive ou rendre le workspace inutilisable pour l’exécution suivante. Si le nettoyage est nécessaire, consignez le processus supprimé, le propriétaire du processus et le moyen de retour arrière, puis relancez un Job de validation avant de remettre la machine dans le pool de production.

05

Quatrième étape : vérifier le service macOS derrière un Runner « Online »

Une connexion SSH réussie prouve que macOS répond ; elle ne prouve pas que le processus Runner est fonctionnel. Le service peut être arrêté, exécuté sous un mauvais compte, privé de son répertoire de travail ou incapable de maintenir la connexion réseau. GitHub recommande de s’appuyer sur les journaux et les mécanismes propres à macOS pour surveiller et dépanner un Runner (guide officiel de surveillance des Runners macOS).

Commencez par recueillir les éléments locaux, sans modifier le service :

whoami
pwd
launchctl print system
ps aux | grep -i Runner.Listener

La sortie doit permettre de répondre à des questions précises :

  • le service tourne-t-il sous le compte prévu ;
  • le processus Runner est-il présent depuis le dernier redémarrage ;
  • le répertoire de travail appartient-il à ce compte ;
  • les journaux montrent-ils une connexion, une déconnexion ou une erreur d’enregistrement ;
  • la session utilisée par launchd possède-t-elle les variables et autorisations attendues ;
  • la connexion réseau reste-t-elle active lorsque personne n’est connecté en SSH.

Le nom exact du service et son domaine de lancement dépendent de l’installation retenue. Il ne faut donc pas copier une commande de suppression trouvée dans un ancien script sans vérifier son effet. Une désinstallation peut retirer le service permanent ; une nouvelle inscription peut rendre l’ancien Runner inutilisable ; la révocation d’un jeton peut empêcher toute restauration immédiate. Avant ces opérations, exportez la configuration nécessaire, conservez les journaux et identifiez le chemin de réinscription autorisé.

La réinstallation ou la réinscription n’est justifiée que si les journaux montrent un état d’enregistrement endommagé, un identifiant devenu invalide ou une installation impossible à démarrer. Si le Runner est sain mais refuse le Job, cette action ne corrigera ni un label absent ni un groupe inaccessible.

06

Cinquième étape : utiliser une matrice de reprise avant de conclure

Une réparation n’est validée que lorsque le routage, l’exécution et le retour du résultat sont tous démontrés. Exécutez les tests dans cet ordre :

  • [ ] Conserver le YAML, les labels, le Runner Group et l’état initial du nœud.
  • [ ] Vérifier que le Job n’est pas bloqué par if, approbation, environnement ou concurrence.
  • [ ] Lancer le Job minimal sur les labels strictement nécessaires.
  • [ ] Confirmer dans les journaux le nom du Runner qui a réellement reçu la tâche.
  • [ ] Exécuter une commande macOS simple et vérifier le retour au statut disponible.
  • [ ] Lancer une compilation Xcode représentative, sans publication irréversible.
  • [ ] Vérifier la récupération de l’archive, des logs et des autres artefacts attendus.
  • [ ] Rejouer un Job nécessitant le label personnalisé de production.
  • [ ] Contrôler que le service revient après un redémarrage planifié.
  • [ ] Documenter la décision : correction, reconstruction isolée ou extension de capacité.

Le premier test mesure le routage. Le second mesure l’exécution locale. Le troisième révèle les dépendances propres à Xcode, au Simulator, à la signature et aux caches. Si le Job minimal échoue, il est inutile d’analyser la durée de compilation. Si le Job minimal réussit mais que la construction échoue, le Runner n’est probablement plus le seul responsable.

La reconstruction d’un nœud est adaptée lorsque l’installation est incohérente, que le service ne peut pas être restauré proprement ou que les labels historiques ne correspondent plus à l’environnement. Elle doit être réalisée sur une machine isolée avant de retirer l’ancien Runner. La documentation GitHub décrit la suppression d’un Runner et ses conséquences ; cette action doit donc être précédée d’une sauvegarde de la configuration et d’une stratégie de retour (procédure officielle de retrait d’un Runner).

L’extension de capacité est le dernier choix, pas le premier réflexe. Elle devient raisonnable lorsque plusieurs Jobs légitimes se disputent en permanence des nœuds correctement routés, que les services récupèrent les tâches et que la file reste présente après les tests de reprise. Dans ce cas, séparez si possible les pools de compilation, de tests Simulator, de publication et de tâches longues. Cette séparation limite les blocages croisés et rend la capacité supplémentaire mesurable.

07

Questions fréquentes sur les Jobs Mac en attente

Pourquoi un Runner auto-hébergé en ligne ne prend-il pas le Job ?

Un état « Online » signifie seulement que GitHub voit le Runner. Le Job peut encore demander des labels absents, cibler un Runner Group interdit au dépôt, attendre une règle de concurrence ou rencontrer un service local qui n’exécute plus correctement le processus. La vérification doit donc commencer par runs-on, les droits du groupe et les journaux, avant tout redémarrage.

Que faire lorsque runs-on correspond mais que le Job reste queued ?

Comparez les labels présents sur le même Runner, puis vérifiez l’autorisation du dépôt à utiliser le Runner Group. Contrôlez ensuite si le nœud est réellement occupé ou si son service est bloqué. Un Job minimal, dépourvu de secrets et de publication, permet de déterminer si la tâche n’est jamais routée ou si elle est routée mais échoue au démarrage.

Pourquoi un Mac Runner Idle ne lance-t-il pas le workflow ?

L’état Idle décrit la disponibilité affichée, mais ne remplace pas l’examen du service local. Les journaux peuvent révéler une déconnexion, un compte incorrect, un répertoire inaccessible ou une session launchd interrompue. Si un redémarrage rétablit temporairement la réception, recherchez la cause de la perte de service et vérifiez la reprise après redémarrage avant d’ajouter un nœud.

Comment corriger l’accès d’un Runner Group à un dépôt ?

Vérifiez le niveau du groupe, le dépôt autorisé et l’organisation propriétaire. Les labels servent à sélectionner un Runner ; ils ne donnent pas à eux seuls le droit d’utiliser ce Runner. Appliquez une autorisation minimale, testez-la avec un workflow sans secret, comparez les Runners visibles avant et après, puis conservez la configuration précédente afin de pouvoir restaurer la politique.

À quel moment ajouter un Runner Mac distant ?

Ajoutez un nœud lorsque les tests prouvent simultanément que le routage fonctionne, que le service récupère les tâches et que tous les Runners admissibles sont réellement mobilisés par des Jobs légitimes. Une file provoquée par un label erroné, une permission manquante ou un service arrêté doit être réparée à la source. Sinon, le nouveau Mac reproduira simplement la panne.

08

Le choix final : réparer le pool actuel ou ajouter un Mac distant

Un parc existant reste préférable lorsque le problème vient d’un YAML trop strict, d’un groupe mal autorisé ou d’un service mal supervisé. Une reconstruction isolée convient lorsque l’enregistrement et le démarrage sont irrécupérables. L’extension, elle, doit répondre à une saturation prouvée et répétée, non à un statut queued apparu après une modification de configuration.

Si l’équipe travaille encore avec un Mac local partagé, une machine Linux qui ne peut pas exécuter Xcode ou une VM macOS difficile à maintenir, elle cumule généralement trois limites : l’environnement n’est pas disponible en permanence, les interfaces et outils Apple ne sont pas reproduits fidèlement, et la reprise après une déconnexion ou une panne dépend d’une personne présente. Pour une reproduction contrôlée du pipeline, un Mac distant réel fournit un environnement plus directement exploitable, avec SSH, accès graphique et possibilité de laisser le nœud en ligne.

Après la vérification des labels, des groupes et du service, une équipe peut utiliser un environnement Mac distant avec NodeMini pour reproduire le même workflow sur une machine isolée, sans confondre un défaut de capacité avec un défaut de routage. Si un nœud de secours est nécessaire pour les compilations Xcode, la location d’un Mac distant pour CI peut être évaluée selon la durée de test, les contraintes d’accès et le besoin d’un fonctionnement continu. L’important est de valider d’abord le Job minimal, la construction réelle et le retour des artefacts ; la location ne devient pertinente qu’une fois la cause technique établie.