Ein registrierter Runner allein macht einen iOS-Build mit GitHub Actions noch nicht produktionsfähig. Für einen zuverlässigen Remote Mac müssen Sie die Xcode-Werkzeugkette festlegen, Signaturgeheimnisse isolieren, Aufgaben über passende Labels routen und anschließend Archive, Export sowie TestFlight-Upload mit einem echten Projekt prüfen.

Diese Anleitung richtet sich an unabhängige Entwickler, die ihren Quellcode bereits in GitHub verwalten und iOS-Builds automatisieren möchten. Sie ist ebenso für Windows- oder Linux-Entwickler ohne lokalen Mac sowie für kleine App-Teams gedacht, die Xcode-Versionen, native Abhängigkeiten und Signaturmaterial kontrollieren müssen.

GitHub Actions iOS-Build beginnt mit einer belastbaren Runner-Basis

Der Remote Mac ist nicht nur ein Rechner, auf dem ein Workflow einmal erfolgreich durchläuft. Er wird zu einem Teil der Veröffentlichungsinfrastruktur. Deshalb sollte vor der ersten YAML-Datei geklärt werden, ob die Maschine dauerhaft erreichbar ist, nach einem Neustart wieder Aufgaben annimmt und genügend freien Speicher für Quellcode, Abhängigkeiten, Archive und temporäre Schlüsselbunddaten besitzt.

Apple legt die jeweils zulässigen Kombinationen aus macOS, Xcode und SDK in den offiziellen Xcode-Systemanforderungen fest. Diese Seite ist die maßgebliche Prüfstelle, wenn ein Projekt ein bestimmtes SDK oder eine aktuelle Upload-Voraussetzung benötigt. Eine pauschale Aussage wie „der Mac kann iOS bauen“ reicht deshalb nicht aus.

1. Systemzustand vor der Registrierung dokumentieren

Erfassen Sie zunächst den Zustand des Remote Mac in einer internen Betriebsnotiz:

  • macOS-Version und Architektur des Hosts,
  • installierte Xcode-Version,
  • aktives SDK und ausgewähltes Entwicklerverzeichnis,
  • verfügbarer Speicherplatz,
  • verwendete Shell und Benutzerkonto,
  • Netzwerkfreigaben für GitHub und App Store Connect,
  • vorgesehener Zweck des Runners, etwa Test-Build oder Veröffentlichung.

Versionsnummern sollten dabei nicht aus einem alten Blogbeitrag übernommen werden. Prüfen Sie die Anforderungen unmittelbar gegen die Apple-Dokumentation, weil sich die akzeptierten Xcode- und SDK-Stände ändern können.

Für die erste Prüfung sollte ein Build ohne Signaturmaterial möglich sein. So lässt sich feststellen, ob Repository-Wiederherstellung, Abhängigkeitsauflösung und Kompilierung funktionieren, bevor Zertifikate oder Provisioning Profiles ins Spiel kommen.

2. Runner auf Repository- oder Organisationsebene registrieren

GitHub unterstützt selbstverwaltete Runner für ein Repository, eine Organisation oder ein Unternehmen. Für ein einzelnes unabhängiges Projekt ist die Repository-Ebene meist die engste Zugriffsumgebung. Ein kleines Team mit mehreren privaten Apps kann dagegen eine Organisation verwenden, sofern die Runner-Gruppen und Zugriffsregeln sauber getrennt werden.

Die offizielle Anleitung zum Hinzufügen eines self-hosted runner liefert die jeweils aktuelle Reihenfolge. Die angezeigten Befehle dürfen nicht unverändert in Dokumentation oder Skripte kopiert werden, wenn sie einen echten Registrierungstoken enthalten.

Ein sicherer Ablauf sieht so aus:

  1. Im vorgesehenen privaten Repository oder in der Organisation die Runner-Einstellungen öffnen.
  2. Das Betriebssystem macOS und die passende Prozessorarchitektur auswählen.
  3. Die von GitHub angezeigten Installationsbefehle auf dem Remote Mac ausführen.
  4. Repository-Adresse, Runner-Token und Arbeitsverzeichnis durch eindeutige Platzhalter ersetzen, sobald der Ablauf dokumentiert wird.
  5. Den Runner-Namen so wählen, dass Zweck und Umgebung erkennbar sind, beispielsweise PUBLISH_MAC_PLACEHOLDER.
  6. Nach der Registrierung prüfen, ob der Runner in GitHub als „Idle“ beziehungsweise verfügbar erscheint.

Ein interaktiver Start im Terminal ist für die Erstdiagnose geeignet, aber nicht für einen unbeaufsichtigten Veröffentlichungsdienst. Wird die Terminal-Sitzung beendet, kann auch der Runner-Prozess enden. Die GitHub-Dokumentation zur Runner-Dienstkonfiguration beschreibt, wie der Prozess als macOS-Dienst eingerichtet und verwaltet wird.

GitHub Actions iOS-Build über Labels an die richtige Maschine senden

Ein Workflow sollte nicht nur nach „self-hosted“ suchen. Dieses allgemeine Label könnte mehrere Maschinen ansprechen, darunter einen Test-Host mit einer anderen Xcode-Basis oder ein System, auf dem keine Veröffentlichung vorgesehen ist.

GitHub verwendet Standardlabels für selbstverwaltete Runner, darunter self-hosted, das Betriebssystemlabel und ein Architekturlabel. Zusätzlich können Sie eigene Labels vergeben. Die GitHub-Erklärung zur Label-Zuordnung beschreibt, wie diese Bedingungen bei der Auswahl zusammenwirken.

Ein Workflow kann beispielsweise so beginnen:

name: iOS Build

on:
  workflow_dispatch:
  push:
    branches:
      - main

jobs:
  archive:
    runs-on:
      - self-hosted
      - macOS
      - ARM64
      - ios-publish-placeholder

    steps:
      - name: Quellcode auschecken
        uses: actions/checkout@v4

      - name: Xcode-Auswahl prüfen
        run: |
          xcode-select -p
          xcodebuild -version

      - name: Abhängigkeiten wiederherstellen
        run: ./scripts/restore-dependencies-placeholder.sh

Der Name ios-publish-placeholder ist absichtlich nur ein Beispiel. Das produktive Label sollte die tatsächliche Verwendung eindeutig beschreiben. Ein Test-Runner und ein Veröffentlichungs-Runner sollten nicht dasselbe Zwecklabel erhalten.

Wie verhält sich ein Workflow ohne passenden Runner?

Wenn kein Runner alle angegebenen Labels erfüllt, wird der Job nicht auf eine beliebige Maschine umgeleitet. Er bleibt in der Warteschlange, bis ein passender Runner verfügbar ist oder der Workflow abgebrochen wird. Das ist sicherer als ein unkontrollierter Fallback, kann aber wie ein hängender Build aussehen.

Planen Sie deshalb eine Beobachtung ein:

  • Prüfen Sie im Workflow- und Repository-Bereich den Warteschlangenstatus.
  • Benennen Sie den Job so, dass Ziel und Zweck sofort sichtbar sind.
  • Definieren Sie einen internen Zeitraum, nach dem ein fehlender Runner untersucht wird.
  • Lassen Sie bei einem Produktionsjob eine Benachrichtigung auslösen, statt unbegrenzt auf eine Maschine zu warten.
  • Dokumentieren Sie, welche Labels zwingend und welche optional sind.

Die konkrete Warte- oder Abbruchdauer ist eine Betriebsentscheidung und sollte nicht als allgemeingültige GitHub-Grenze dokumentiert werden.

Die Xcode-Werkzeugkette als unveränderliche Build-Basis festlegen

Mehrere Xcode-Installationen auf demselben Mac sind praktisch, aber riskant. Ein Aufruf von xcodebuild kann eine andere Entwicklerumgebung verwenden als erwartet, wenn xcode-select oder die Shell-Umgebung nicht eindeutig gesetzt ist.

3. Aktives Entwicklerverzeichnis und Scheme festschreiben

Legen Sie im Workflow oder in einem kontrollierten Vorbereitungsskript das gewünschte Entwicklerverzeichnis fest:

sudo xcode-select --switch "/Applications/Xcode_PLACEHOLDER.app"
xcode-select -p
xcodebuild -version

Der Pfad ist ein Platzhalter und muss an die geprüfte Installation angepasst werden. Das Workflow-Protokoll sollte den ausgewählten Pfad und die Xcode-Ausgabe speichern, damit ein späterer Fehler nicht nur mit „Build fehlgeschlagen“ beschrieben wird.

Zusätzlich müssen Projektdateien und Abhängigkeiten reproduzierbar sein:

  • Verwenden Sie die vorhandene Lock-Datei für Swift Package Manager oder andere Paketmanager.
  • Prüfen Sie, ob das erwartete Workspace- oder Projektformat ausgecheckt wurde.
  • Geben Sie das Scheme ausdrücklich an, statt auf eine zufällige Standardeinstellung zu vertrauen.
  • Halten Sie Bundle-ID, Team-ID und Export-Konfiguration in einer kontrollierten Konfiguration, jedoch niemals mit privaten Schlüsselmaterialien im Repository.
  • Führen Sie zuerst einen nicht signierten Kompilierungstest aus.

Ein minimalistischer Prüfpunkt kann folgendermaßen aussehen:

xcodebuild \
  -workspace "APP_WORKSPACE_PLACEHOLDER.xcworkspace" \
  -scheme "APP_SCHEME_PLACEHOLDER" \
  -sdk iphoneos \
  -configuration Release \
  CODE_SIGNING_ALLOWED=NO \
  build

Ob ein Projekt ohne Signierung vollständig gebaut werden kann, hängt von seinen Abhängigkeiten und Build-Skripten ab. Der Test soll nicht den späteren Release-Build ersetzen, sondern Fehler in Checkout, Paketauflösung und Compilerumgebung von Signaturproblemen trennen.

4. Archive und Export nicht als denselben Zustand behandeln

Ein erfolgreiches Archive beweist, dass Xcode die App mit der gewählten Konfiguration verarbeiten konnte. Es beweist noch nicht, dass die richtige Signatur exportiert wurde oder dass App Store Connect das Ergebnis akzeptiert.

Apple beschreibt in den Dokumenten zu Zertifikaten und Provisioning Profiles die Rolle von Signaturzertifikaten und Profilen. Für die Veröffentlichung muss die Zuordnung aus Bundle-ID, Team, Zertifikat, Profil und Export-Methode zusammenpassen.

Danach wird das signierte Ergebnis zu App Store Connect übertragen. Die Apple-Anleitung zum Upload von Builds trennt den Upload vom anschließenden Verarbeitungsstatus. Ein Upload ohne Fehlermeldung ist daher nicht automatisch ein in TestFlight verfügbares Build.

Signaturmaterial strikt vom normalen Build trennen

Ein selbstverwalteter Runner besitzt mehr dauerhafte Zugriffsmöglichkeiten als ein kurzlebiger, fremd verwalteter Build-Prozess. Wer ihn für ein öffentliches Repository oder für Workflows mit unkontrollierten Beiträgen verwendet, riskiert, dass fremder Code auf dem Host ausgeführt wird.

GitHub weist in den Sicherheitshinweisen für selbstverwaltete Runner ausdrücklich auf dieses Modell hin. Ein Veröffentlichungs-Runner sollte daher nur für vertrauenswürdige private Repositories, klar definierte Branches und freigegebene Workflows zugelassen werden.

Wo liegen Zertifikate und Profile sicher?

GitHub Secrets sind für die Übergabe von Zugangsdaten geeignet, aber sie ersetzen keine Sicherheitsarchitektur. Ein Code-Signaturzertifikat mit privatem Schlüssel, ein Provisioning Profile und ein App-Store-Connect-Zugang erfüllen unterschiedliche Aufgaben und müssen getrennt behandelt werden.

Bewährt hat sich folgende Trennung:

  • Signaturzertifikat und privater Schlüssel werden nur für den erforderlichen Job bereitgestellt.
  • Das Provisioning Profile wird erst während des Release-Schritts importiert.
  • App-Store-Connect-Anmeldedaten werden als eigene Secrets oder als separat verwaltete Zugangsdaten behandelt.
  • Bundle-ID, Team-ID und Zertifikatsname bleiben Platzhalter in öffentlichen Beispielen.
  • Der temporäre Schlüsselbund erhält ein zufälliges, nur im Job verfügbares Passwort.
  • Nach dem Export werden importierte Profile, Schlüsselbund und temporäre Dateien entfernt.
  • Secrets werden nach einer Änderung der Team- oder Projektberechtigungen rotiert.

Ein einzelner API-Schlüssel kann die Code-Signatur nicht ersetzen. Für Build-Signierung und Upload gelten verschiedene Berechtigungs- und Dateitypen. Diese Trennung sollte im Workflow sichtbar sein, etwa durch getrennte Schritte und klar benannte Protokollabschnitte.

5. Temporären Schlüsselbund erzeugen und nach dem Job entfernen

Eine Dokumentation darf keine echten Werte enthalten. Ein schematischer Ablauf sieht so aus:

security create-keychain \
  -p "$KEYCHAIN_PASSWORD_PLACEHOLDER" \
  "TEMP_KEYCHAIN_PLACEHOLDER.keychain-db"

security unlock-keychain \
  -p "$KEYCHAIN_PASSWORD_PLACEHOLDER" \
  "TEMP_KEYCHAIN_PLACEHOLDER.keychain-db"

# Zertifikat und Profil aus geschützten Secrets importieren
# Keine echten Dateinamen, Passwörter oder Schlüssel im Repository speichern

security delete-keychain \
  "TEMP_KEYCHAIN_PLACEHOLDER.keychain-db"

Der reale Workflow muss zusätzlich sicherstellen, dass die Löschung auch bei einem Fehler ausgeführt wird. Prüfen Sie außerdem, ob Shell-Logs, Diagnosearchive oder Cache-Verzeichnisse versehentlich exportierte Geheimnisse enthalten.

Die gesamte Veröffentlichungsstrecke in getrennten Zuständen prüfen

Ein produktiver Workflow sollte mindestens diese Zustände unterscheiden:

  1. Runner nimmt den Job an.
  2. Repository und Abhängigkeiten werden wiederhergestellt.
  3. Das Projekt wird mit der festgelegten Xcode-Umgebung kompiliert.
  4. Xcode erzeugt ein Archive.
  5. Das Archive wird signiert und in das vorgesehene Format exportiert.
  6. Der Export wird an App Store Connect übertragen.
  7. App Store Connect verarbeitet den Build und stellt ihn für TestFlight bereit.

Damit lässt sich die Fehlerquelle eingrenzen. Ein Fehler beim Checkout ist kein Xcode-Fehler. Ein fehlendes Zertifikat ist kein Netzwerkfehler. Ein erfolgreicher Upload ist noch kein abgeschlossener App-Store-Connect-Verarbeitungsvorgang.

Die Artefaktregeln sollten ebenfalls festgelegt werden. Speichern Sie nur die für die Fehlersuche notwendigen Logs, das Archive oder den Export nach den internen Datenschutzregeln und Debug-Symbole nach einer klaren Aufbewahrungsfrist. Enthalten Logs möglicherweise Pfade, Teamdaten oder Signaturinformationen, müssen sie vor einer Weitergabe bereinigt werden.

Wiederherstellung nach Neustart und Abbruch operationalisieren

Wie bleibt der Runner nach einem Neustart verfügbar?

Ein Dienststart ist nur der erste Teil der Wiederherstellung. Nach einem Neustart muss geprüft werden, ob der Runner tatsächlich mit GitHub verbunden ist und nicht nur ein lokaler Prozess läuft.

Führen Sie diese Wiederherstellungsprüfung aus:

  1. Den Remote Mac kontrolliert neu starten.
  2. Nach dem Systemstart den Dienststatus lokal und den Runner-Status in GitHub prüfen.
  3. Einen ungefährlichen manuellen Workflow an den betreffenden Labels auslösen.
  4. Einen Build-Abbruch simulieren und kontrollieren, ob temporäre Schlüsselbunddaten entfernt werden.
  5. Den Arbeitsbereich auf zurückgebliebene Archive, Profile und Cache-Fehler untersuchen.
  6. Bei einer beschädigten Registrierung den Runner nach dokumentiertem Verfahren entfernen und neu registrieren.
  7. Die Änderung mit Datum, Grund und verantwortlicher Person protokollieren.

Die GitHub-Anleitung zur Überwachung und Fehlerbehebung auf macOS ist hierfür die geeignete Referenz. Sie sollten sich nicht allein auf einen grünen Workflow verlassen, wenn der Runner danach offline bleibt.

Bei stark wachsendem Speicherverbrauch muss die Ursache getrennt betrachtet werden: Xcode-Archive, Paket-Caches, Derived Data, Runner-Arbeitsverzeichnisse und Diagnoseprotokolle haben unterschiedliche Löschregeln. Automatisches Löschen ohne Aufbewahrungskonzept kann zwar Speicher freigeben, aber die Fehlersuche erschweren.

Die Veröffentlichung mit einer Checkliste abnehmen

Vor einer dauerhaften Nutzung sollte ein vollständig reproduzierbarer Test mit einem nicht kritischen Projekt oder einer dafür vorgesehenen Test-App stattfinden. Die folgenden Punkte sollten einzeln abgehakt werden:

  • [ ] macOS, Xcode, SDK und Architektur entsprechen den aktuellen Apple-Anforderungen.
  • [ ] Der Runner ist auf der richtigen Repository- oder Organisationsebene registriert.
  • [ ] Ein Dienst startet nach einem Neustart automatisch.
  • [ ] Der Runner trägt nur die für seinen Zweck notwendigen Labels.
  • [ ] Ein Job ohne passendes Label bleibt nachvollziehbar in der Warteschlange.
  • [ ] Der Workflow wählt das Entwicklerverzeichnis ausdrücklich aus.
  • [ ] Lock-Dateien, Workspace und Scheme werden reproduzierbar verwendet.
  • [ ] Ein Build ohne Signaturmaterial wurde erfolgreich ausgeführt.
  • [ ] Zertifikat, privater Schlüssel und Provisioning Profile werden getrennt behandelt.
  • [ ] Secrets sind nicht in YAML, Quellcode, Logs oder Artefakten enthalten.
  • [ ] Archive, Export und Upload erscheinen als getrennte Prüfschritte.
  • [ ] Der App-Store-Connect-Verarbeitungsstatus wird nach dem Upload geprüft.
  • [ ] Ein fehlgeschlagener Job entfernt temporäre Zugangsdaten.
  • [ ] Runner-Update, Neu-Registrierung und Zugriffsentzug sind dokumentiert.
  • [ ] Öffentliche Pull Requests können den Veröffentlichungs-Runner nicht unkontrolliert verwenden.

Die Freigabe sollte am Ende nicht nur „bestanden“ oder „fehlgeschlagen“ lauten. Sinnvoll sind drei Ergebnisse: produktionsgeeignet, mit konkreten Korrekturen nachzubessern oder für einen dauerhaften Veröffentlichungsdienst ungeeignet.

Welche Betriebsform passt zum Projekt?

Ein eigener lokaler Mac bietet direkten Zugriff auf angeschlossene Geräte und ist für tägliche Entwicklung mit Simulator, Debugger und manuellen Tests bequem. Dafür muss die Hardware dauerhaft eingeschaltet, aktualisiert, gesichert und vor unbefugtem Zugriff geschützt werden.

Ein Remote Mac mit selbstverwaltetem Runner ist besonders interessant, wenn eine unabhängige Person nur für Builds, Signierung und Veröffentlichung eine macOS-Umgebung benötigt. Die Entscheidung hängt jedoch davon ab, ob der Anbieter Neustarts, Zugriffsschutz und eine stabile Übergabe tatsächlich unterstützt. Informationen zu verfügbaren RUVCLOUD-Mac-Mietmodellen sollten deshalb erst nach der technischen Abnahme mit dem konkreten Projekt abgeglichen werden.

Option Stärken Grenzen Geeignet, wenn
Lokaler Mac Direkte Bedienung, physische Geräte und lokale Fehlersuche Anschaffung, Wartung und dauerhafte Verfügbarkeit liegen beim Team Tägliche Entwicklung und Hardwaretests im Mittelpunkt stehen
Selbstverwalteter Runner auf Remote Mac macOS-Werkzeugkette für CI/CD, getrennte Build-Umgebung und planbare Nutzung Netzwerk, Zugriffsschutz, Neustart und Speicher müssen überwacht werden Archive und Veröffentlichungen regelmäßig automatisiert werden sollen
Kurzlebige externe Build-Umgebung Weniger dauerhafte Hostpflege Weniger Kontrolle über Zustand, Caches und spezielle native Abhängigkeiten Standardisierte Builds ohne dauerhafte Host-Anpassungen ausreichen
Abnahmekriterium Nachweis Konsequenz bei Fehler
Runner-Verfügbarkeit Status nach Dienststart und Neustart Kein produktiver Dauerbetrieb
Xcode-Konsistenz Gespeicherte Ausgabe von xcode-select und xcodebuild Toolchain korrigieren, bevor Signaturmaterial verwendet wird
Signatursicherheit Temporärer Schlüsselbund, getrennte Secrets und Bereinigung Release-Zugriff sperren und Workflow überarbeiten
Artefaktkette Getrennte Logs für Archive, Export, Upload und Verarbeitung Fehlerquelle gezielt statt pauschal untersuchen
Routing Test mit passenden und unpassenden Labels Labels oder Runner-Gruppen neu definieren
Wiederherstellung Kontrollierter Neustart und anschließender Testjob Host nicht als dauerhafter Veröffentlichungsdienst einsetzen

Für Windows- oder Linux-basierte Ausgangsumgebungen sind lokale Workarounds langfristig oft mit drei konkreten Nachteilen verbunden: Xcode bleibt nicht nativ verfügbar, Signierung und App-Store-Connect-Schritte müssen über eine zusätzliche macOS-Grenze laufen, und ein eigener Rechner muss für automatisierte Builds dauerhaft gepflegt werden. Ein gemieteter Mac von RUVCLOUD kann in diesem Fall die praktischere Zwischenlösung sein, wenn zunächst nur ein echter Release-Prozess geprüft oder eine verlässliche CI/CD-Maschine benötigt wird. Wer die Umgebung ausprobieren möchte, kann den passenden RUVCLOUD-Bestellweg für eine Remote-Mac-Umgebung nach erfolgreicher technischer Prüfung auswählen.

Der sinnvolle nächste Schritt ist nicht die sofortige Umstellung aller Veröffentlichungen. Erstellen Sie zuerst den Workflow mit Platzhaltern, führen Sie auf einem kurzfristig gemieteten Remote Mac Archive, Signierung und Upload mit einem echten Projekt aus und dokumentieren Sie jeden Zustand. Erst wenn auch Neustart, Bereinigung, Zugriffsbeschränkung und Fehlerdiagnose bestanden sind, sollte dieser Mac als dauerhafter GitHub-Actions-Runner für produktive iOS-Builds dienen.