Un Runner auto-hébergé peut être enregistré sur macOS et exécuté comme service, selon la documentation officielle de GitHub sur la configuration du Runner. La conclusion opérationnelle est donc claire : la compilation iOS avec GitHub Actions est possible sur un Mac distant, mais l’enregistrement du Runner ne suffit pas pour une publication fiable. Il faut également figer l’outil Xcode, isoler les secrets de signature, contrôler le routage des tâches et valider séparément l’Archive, l’export et l’envoi vers TestFlight.

Cet article s’adresse aux développeurs indépendants qui gèrent déjà leur code avec GitHub et veulent automatiser la construction d’une application iOS. Il concerne aussi les développeurs Windows ou Linux sans Mac local, ainsi que les petites équipes qui doivent maîtriser une version de Xcode, des dépendances natives ou des certificats de publication.

Le périmètre réel d’un Runner de publication

Un Mac distant utilisé par GitHub Actions n’est pas seulement un ordinateur capable de lancer une commande xcodebuild. Dans un environnement de production, il devient un maillon permanent entre le dépôt, le système macOS, Xcode, les certificats Apple et App Store Connect.

Cette distinction permet d’éviter une erreur fréquente : constater qu’un premier job est passé au vert, puis considérer que l’environnement est terminé. Un job peut réussir la compilation tout en échouant au moment de créer une archive distribuable, d’importer la clé privée, de sélectionner le bon profil de provisioning ou de transmettre le build à Apple.

Pour un projet audio, vidéo ou de design, cette séparation est encore plus importante. Les bibliothèques natives, les ressources lourdes, les codecs, les extensions d’application et les outils graphiques peuvent dépendre d’un environnement macOS précis. Le Runner doit donc être évalué sur sa capacité à reproduire le projet, pas uniquement sur sa capacité à rester connecté.

Les conditions de base sont les suivantes :

  • le Mac doit être compatible avec la version de Xcode et le SDK ciblés ;
  • le stockage doit permettre la récupération des dépendances, les données dérivées, l’archive et les symboles de débogage ;
  • le réseau doit rester disponible pendant la récupération du code, la résolution des paquets et l’envoi vers App Store Connect ;
  • le compte utilisé pour le Runner doit être contrôlé, particulièrement si la machine conserve des secrets ;
  • le processus Runner doit reprendre après une déconnexion de session ou un redémarrage de l’hôte.

La page des exigences système de Xcode publiée par Apple doit être consultée avant toute installation. Les versions de macOS, de Xcode et du SDK ne doivent pas être déduites d’un ancien tutoriel : Apple peut modifier les exigences de soumission et les versions acceptées.

L’enregistrement et la disponibilité du Runner

L’installation commence au niveau du dépôt ou de l’organisation GitHub. Le choix dépend de la portée voulue : un Runner rattaché à un dépôt convient à un projet isolé ; un Runner d’organisation peut servir plusieurs dépôts, mais augmente immédiatement le risque d’exposition des secrets.

La procédure officielle d’ajout d’un Runner auto-hébergé fournit la commande d’enregistrement, le répertoire de travail et les paramètres associés. Les valeurs réellement sensibles doivent rester hors du dépôt. Dans un exemple de documentation interne, les éléments suivants doivent toujours être remplacés par des marqueurs évidents :

./config.sh \
  --url https://github.com/__ORGANISATION__/__DEPOT__ \
  --token __RUNNER_TOKEN__ \
  --name __RUNNER_NAME__ \
  --labels __CUSTOM_LABEL__

Le jeton d’enregistrement n’est pas une valeur à copier dans un fichier de workflow. Il doit être fourni au moment de l’installation, puis retiré des historiques de terminal, des captures d’écran et des journaux partagés.

Une session interactive lancée avec une commande de type run.sh est utile pour vérifier la première connexion, mais elle n’est pas suffisante pour un serveur de compilation. Si la fenêtre de terminal est fermée ou si la session distante disparaît, le processus peut s’arrêter avec elle. La configuration comme service macOS répond à un autre objectif : permettre au Runner de démarrer avec l’hôte et de fonctionner sans dépendre d’une session VNC ouverte.

Après l’installation du service, le contrôle ne doit pas se limiter à l’icône « connecté » dans GitHub. Il faut aussi :

  • redémarrer le Mac distant ;
  • vérifier que le service démarre réellement ;
  • confirmer que le Runner apparaît comme disponible ;
  • lancer un job de test après une nouvelle connexion ;
  • inspecter les journaux si le Runner reste hors ligne.

La documentation GitHub consacrée à la surveillance et au dépannage sur macOS détaille les points à examiner lorsqu’un Runner ne prend pas de tâche.

Le routage par architecture et par usage

Un workflow qui cible simplement self-hosted peut envoyer une tâche vers un Runner inattendu. Cette étiquette décrit la famille de Runner, mais elle ne garantit ni le système d’exploitation, ni l’architecture, ni l’usage de publication.

Les étiquettes par défaut et personnalisées doivent donc exprimer une intention précise. Un job iOS peut par exemple demander self-hosted, macOS et ARM64, puis ajouter une étiquette telle que ios-release pour séparer la publication des compilations de développement.

jobs:
  archive:
    runs-on:
      - self-hosted
      - macOS
      - ARM64
      - ios-release
    steps:
      - uses: actions/checkout@v4
      - name: Vérifier l’environnement
        run: |
          sw_vers
          xcode-select -p
          xcodebuild -version

Les valeurs liées à l’environnement doivent rester cohérentes avec les étiquettes. Une étiquette ARM64 apposée à un hôte qui ne correspond pas à cette architecture créerait une promesse trompeuse dans le routage. De la même manière, l’étiquette ios-release ne doit pas être attribuée à une machine utilisée pour des essais avec du code non approuvé.

Les règles officielles de routage par étiquettes GitHub expliquent que toutes les étiquettes demandées par le job doivent correspondre à un Runner disponible. Si aucune combinaison ne correspond, le job reste en attente au lieu d’être exécuté sur une autre machine. Ce comportement doit être observable : une alerte ou une vérification régulière doit signaler qu’un job de publication attend un Runner absent.

Le fichier de workflow doit également refuser les chemins ambigus. Une branche de développement ne devrait pas pouvoir appeler automatiquement un Runner qui possède les certificats de distribution. La sélection du Runner et la protection de la branche doivent être conçues ensemble.

La cohérence de Xcode et des dépendances

La première compilation utile est une compilation sans signature. Elle sert à prouver que le dépôt peut être récupéré, que les dépendances sont restaurées et que le projet se compile avec le bon schéma. Cette étape réduit le risque de confondre un problème de code ou de dépendance avec un problème de certificat.

Lorsque plusieurs versions de Xcode sont présentes, le workflow doit sélectionner explicitement le dossier développeur actif :

sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer
xcodebuild -version
xcodebuild -showsdks

Le chemin ci-dessus n’est qu’un exemple de structure. Le nom réel de l’application Xcode doit être défini par la politique de l’environnement et vérifié avant le job. Le point important est que le workflow affiche la version utilisée au début de chaque exécution, au lieu de faire confiance à l’état laissé par une session précédente.

La reproductibilité dépend aussi de plusieurs éléments souvent négligés :

  • le fichier de verrouillage des dépendances doit être versionné ;
  • le schéma utilisé pour l’Archive doit être partagé et marqué comme nécessaire à la compilation ;
  • les réglages de signature doivent être cohérents avec la cible réellement publiée ;
  • les caches doivent pouvoir être invalidés lorsqu’une version de Xcode ou d’un paquet change ;
  • les données dérivées ne doivent pas être considérées comme une preuve de compilation propre.

Pour les projets Swift Package Manager, CocoaPods ou intégrant des bibliothèques natives, il est préférable de distinguer la restauration des dépendances de la compilation. Un échec de résolution réseau, un paquet indisponible ou un cache corrompu doit apparaître comme tel dans les journaux.

La compatibilité entre Xcode, macOS, SDK et exigences d’envoi doit être contrôlée dans les exigences actuelles de Xcode chez Apple. Aucun guide publié antérieurement ne doit remplacer cette vérification.

La séparation des secrets de signature

La signature iOS ne repose pas sur une seule clé API. Le projet peut avoir besoin d’un certificat de distribution avec sa clé privée, d’un profil de provisioning associé, d’identifiants d’équipe et d’un moyen d’authentification pour l’envoi vers App Store Connect.

Apple décrit les rôles respectifs des certificats et profils de provisioning. Ces éléments doivent être traités séparément des identifiants d’App Store Connect. Une clé destinée à l’envoi ne remplace pas le matériel cryptographique requis pour signer l’application.

Une organisation raisonnable peut suivre ce flux :

  • enregistrer les secrets dans les secrets GitHub avec le périmètre le plus réduit possible ;
  • limiter le Runner de publication à un dépôt privé et à des branches protégées ;
  • décoder les éléments nécessaires uniquement pendant le job ;
  • créer un trousseau temporaire avec un mot de passe généré pour la tâche ;
  • importer le certificat et la clé privée dans ce trousseau ;
  • utiliser le profil de provisioning dans un répertoire temporaire ;
  • supprimer le trousseau, les profils et les fichiers intermédiaires après l’export ;
  • faire tourner les certificats et identifiants selon une procédure documentée.

Les secrets ne doivent apparaître ni dans une commande affichée, ni dans une variable imprimée, ni dans un artefact. Les journaux doivent être inspectés après un échec, car un outil de signature peut produire un chemin local ou un message contenant trop d’informations.

Un Runner auto-hébergé est particulièrement sensible si le dépôt accepte du code provenant de contributeurs externes. Les recommandations de sécurité GitHub pour les Runners auto-hébergés indiquent pourquoi une machine persistante ne doit pas exécuter sans contrôle du code non fiable. Pour un dépôt public, il est préférable de séparer les tâches de validation et les tâches de publication, voire d’utiliser un environnement de publication qui ne reçoit jamais les travaux issus de demandes de contribution non approuvées.

L’acceptation par état du pipeline

Un pipeline iOS fiable ne possède pas un seul résultat « réussi ». Il doit rendre visibles plusieurs états : récupération du code, restauration des dépendances, compilation, création de l’Archive, export signé, transfert vers App Store Connect et traitement côté Apple.

L’Archive est le produit intermédiaire qui rassemble l’application compilée et les informations nécessaires à sa distribution. L’export transforme cette archive selon la méthode de distribution sélectionnée. L’envoi transfère ensuite le build à Apple, mais la disponibilité dans TestFlight peut dépendre d’un traitement ultérieur.

La documentation Apple sur l’envoi des builds vers App Store Connect doit servir de référence pour cette dernière partie. Le workflow doit conserver, selon la politique du projet :

  • les journaux de compilation et d’export ;
  • l’archive destinée au diagnostic, lorsque sa conservation est acceptable ;
  • les symboles de débogage nécessaires à l’analyse des rapports de crash ;
  • l’identifiant de build et les informations de version ;
  • le résultat explicite de l’outil d’envoi.

Un exemple de structure de workflow peut séparer les responsabilités :

- name: Compiler sans signature
  run: xcodebuild -scheme __SCHEME__ -configuration Release build CODE_SIGNING_ALLOWED=NO

- name: Créer l’Archive
  run: xcodebuild archive -scheme __SCHEME__ -archivePath "$RUNNER_TEMP/__APP__.xcarchive"

- name: Exporter l’application signée
  run: xcodebuild -exportArchive \
         -archivePath "$RUNNER_TEMP/__APP__.xcarchive" \
         -exportOptionsPlist "$RUNNER_TEMP/__EXPORT_OPTIONS__.plist" \
         -exportPath "$RUNNER_TEMP/export"

- name: Envoyer vers App Store Connect
  run: __UPLOAD_COMMAND_WITHOUT_INLINE_SECRETS__

Les noms de projet, de schéma, de Bundle ID, d’équipe, de certificat, de chemin et de clé doivent rester des espaces réservés dans les exemples. Une commande qui retourne un code de sortie positif confirme seulement que cette commande s’est terminée comme prévu ; elle ne prouve pas que l’archive est visible dans TestFlight.

La récupération après incident

Un Mac distant destiné à la compilation doit être traité comme une infrastructure, même lorsqu’il sert un seul développeur. Les contrôles de récupération portent sur le Runner, macOS, le stockage, les caches, les dépendances et les accès.

Le registre d’incidents doit préciser :

  • comment vérifier l’état du service après un redémarrage ;
  • comment réinstaller ou réinscrire le Runner ;
  • comment révoquer l’ancien accès si la machine a été remplacée ;
  • comment nettoyer les archives et données dérivées qui occupent le stockage ;
  • comment invalider un cache après une mise à jour de Xcode ;
  • comment reprendre un job interrompu sans réutiliser un trousseau incomplet ;
  • comment identifier si l’échec vient de la compilation, de la signature, du transfert ou du traitement Apple.

Le Runner ne doit pas être considéré comme disponible simplement parce que le Mac répond au VNC ou au SSH. La condition utile est sa capacité à accepter une tâche conforme aux étiquettes, à exécuter le workflow dans un environnement connu et à produire des journaux exploitables.

Comparaison des architectures de Runner

Le tableau suivant aide à choisir la portée et le rôle du Runner avant l’installation. Il ne remplace pas les contrôles de sécurité : une architecture techniquement fonctionnelle peut rester inadaptée à la publication.

Option Usage adapté Avantage principal Risque ou limite Décision recommandée
Runner de dépôt Projet iOS unique Périmètre d’accès lisible Peu pratique pour mutualiser plusieurs applications Choisir cette option pour un indépendant ou un dépôt sensible
Runner d’organisation Plusieurs dépôts privés contrôlés Gestion centralisée des étiquettes Risque de mélange entre projets et secrets L’utiliser seulement avec des règles d’accès strictes
Runner de compilation Tests, compilation sans signature, vérification des dépendances Exposition limitée des secrets Ne publie pas directement Préférer cette séparation pour les branches de développement
Runner de publication Archive, export signé, envoi TestFlight Chaîne de livraison complète Cible très sensible Réserver aux branches et flux approuvés
Runner interactif Installation et diagnostic initial Lecture immédiate des journaux S’arrête avec la session Le limiter au dépannage, jamais à la production
Runner configuré comme service Tâches régulières et reprise après redémarrage Fonctionne sans session distante ouverte Demande une maintenance du service Choisir cette option pour un Mac utilisé comme serveur

Pour un indépendant, un Runner de dépôt dédié peut être le compromis le plus compréhensible. Pour une petite équipe, la séparation entre compilation et publication est souvent plus importante que la mutualisation de la machine.

Tableau d’acceptation du flux de publication

Avant de transformer le Mac en serveur permanent, chaque ligne doit être vérifiée sur un projet de test dépourvu de secrets réels. Les valeurs propres au projet doivent rester masquées dans les journaux.

Contrôle Preuve attendue Résultat acceptable Action en cas d’échec
Disponibilité du Mac Runner visible et joignable Le job reçoit la tâche correspondant à ses étiquettes Vérifier le service et le réseau
Routage Étiquettes affichées dans le job La tâche ne part pas vers un environnement de test Corriger runs-on et les étiquettes
Outil Xcode Version et chemin affichés Le workflow utilise le Xcode prévu Fixer DEVELOPER_DIR ou xcode-select
Dépendances Journaux de restauration et fichiers verrouillés La restauration est reproductible Réparer le verrouillage ou invalider le cache
Compilation Résultat sans signature Le code compile avant l’ajout des secrets Corriger le projet ou l’environnement
Archive Fichier .xcarchive exploitable L’archive correspond au schéma publié Vérifier le schéma et les réglages de build
Export Paquet signé produit Le profil et le certificat correspondent à la cible Réexaminer la chaîne de signature
Transfert Résultat de l’outil d’envoi App Store Connect accepte le build Vérifier les droits et les identifiants
Traitement Apple Build visible dans App Store Connect Le statut est suivi séparément du job GitHub Consulter les messages côté Apple
Reprise Service actif après redémarrage Une nouvelle tâche peut être reçue Réinstaller le service ou réinscrire le Runner
Nettoyage Trousseau et fichiers temporaires absents Aucun secret persistant inutilement Ajouter une étape de nettoyage et contrôler les journaux

Cette grille est aussi utile pour les applications qui intègrent des éléments créatifs. Une application audio peut nécessiter des ressources natives et des bibliothèques spécifiques ; une application vidéo peut produire des artefacts volumineux ; un projet de design peut dépendre de polices, de plugins ou de traitements d’images. Dans chacun de ces cas, le contrôle doit confirmer que les dépendances sont récupérées de manière reproductible et que le stockage est surveillé, plutôt que de supposer qu’un build local réussi sera identique sur le Runner.

Les développeurs qui ne souhaitent pas maintenir immédiatement ce type d’environnement peuvent examiner les formules Mac de RUVCLOUD, puis commencer par une période courte consacrée à l’acceptation du projet. Une machine distante ne devient une bonne solution que si elle passe les contrôles techniques du dépôt concerné.

Questions fréquentes

GitHub Actions peut-il construire une application iOS sur un Mac distant ?

Oui, à condition que le Mac distant exécute macOS compatible avec Xcode et qu’un Runner auto-hébergé y soit enregistré. Le workflow doit cibler les bonnes étiquettes, tandis que le service Runner doit rester disponible sans session VNC active. La construction, la signature et l’envoi doivent ensuite être validés séparément.

Comment figer Xcode sur un Runner auto-hébergé ?

Le projet doit documenter la version attendue, le chemin du développeur actif et les dépendances verrouillées. Le workflow peut sélectionner explicitement Xcode, puis afficher xcodebuild -version avant la compilation. Si plusieurs installations coexistent, une sélection implicite est trop fragile pour une publication.

Où conserver les certificats de signature dans GitHub Actions ?

Les certificats, clés privées et profils de provisioning doivent être stockés comme secrets à portée limitée, jamais dans le dépôt. Ils doivent être importés dans un trousseau temporaire pendant le job, puis supprimés. Les identifiants App Store Connect doivent être gérés séparément, car ils ne remplacent pas les éléments nécessaires à la signature.

Comment restaurer automatiquement le Runner après un redémarrage du Mac ?

Le Runner doit être installé comme service macOS. Après le redémarrage, il faut contrôler son état, sa présence dans GitHub, ses étiquettes et sa capacité à recevoir un job de test. Une procédure documentée doit également prévoir la révocation de l’ancien Runner si l’hôte doit être remplacé ou réinscrit.

Une compilation réussie est-elle immédiatement disponible dans TestFlight ?

Non. La compilation, l’Archive, l’export signé, l’envoi et le traitement dans App Store Connect sont des états différents. Le workflow doit produire une preuve pour chacun d’eux. Le build ne doit être annoncé comme disponible dans TestFlight qu’après vérification de son statut côté App Store Connect.

Choix de l’hôte et mise en production

Un Mac local donne un accès direct aux périphériques, aux tests manuels et aux réglages de l’équipe, mais il doit rester allumé, entretenu et disponible lorsque GitHub Actions lance une tâche. Un Runner hébergé sur un ordinateur personnel peut aussi exposer des fichiers personnels ou des identifiants si le cloisonnement est insuffisant.

Un environnement distant évite l’achat d’une machine dédiée et permet de préparer un serveur de compilation accessible depuis Windows ou Linux. En contrepartie, il faut accepter la dépendance au réseau, vérifier les limites d’accès matériel et organiser la gestion des données temporaires. Pour une application qui nécessite un iPhone physiquement connecté, un périphérique audio spécialisé ou une validation graphique interactive, un Mac local ou un laboratoire matériel peut rester plus approprié.

Dans le cas d’un Runner permanent, la location ne dispense pas de l’architecture : elle fournit un hôte macOS à intégrer dans une procédure contrôlée. Le provisionnement d’un Mac distant RUVCLOUD peut être envisagé pour tester d’abord un projet réel, notamment lorsqu’un développeur Windows ou Linux doit valider Xcode, l’Archive et TestFlight sans acheter immédiatement une machine.

Une fois le workflow préparé, la décision doit suivre une règle simple : si la compilation sans signature, la sélection de Xcode, la signature, l’envoi, la reprise après redémarrage et le nettoyage des secrets sont vérifiés, le Mac peut passer en usage permanent. Si l’un de ces contrôles échoue, il vaut mieux conserver un Runner de test et corriger l’environnement avant de lui confier une publication réelle.