Von der Verbindung zum ersten Build-Artefakt

Die erste Stunde
mit Ihrem Mac

Nutzen Sie die erste halbe Stunde für Verbindung, Toolchain-Prüfung und den ersten Build. In der zweiten Hälfte kümmern Sie sich um Signaturen und Pipeline. Sie erhalten einen exklusiven physischen Mac statt einer virtuellen Maschine – Ihre gewohnte macOS-Oberfläche und Kommandozeile stehen Ihnen weiterhin zur Verfügung.

Schon bestellt? Rufen Sie im Kundenportal die tatsächliche Host-Adresse, den Port, den Benutzernamen und die Bereitstellungsdaten ab. Verwenden Sie Beispielwerte nicht als Zugangsdaten.

00–10 Min.

Host-Fingerabdruck prüfen und per SSH verbinden.

10–20 Min.

Xcode, Projektabhängigkeiten und Speicherplatz prüfen.

20–30 Min.

Zuerst ohne Signatur testen, dann das Archive prüfen.

Schritt 1 · Verbindung prüfen

Befehle per SSH, Oberfläche per Bildschirmfreigabe

Erstellen Sie den Schlüssel lokal und übertragen Sie ausschließlich den öffentlichen Schlüssel. Der private Schlüssel bleibt auf Ihrem Gerät und sollte mit einer Passphrase geschützt sein. Für Host, Port und Benutzername gelten die im Kundenportal angegebenen Bereitstellungsdaten.

Im lokalen macOS-Terminal ausführen
read -r -p "Host-Adresse: " MAC_HOST
read -r -p "SSH-Port: " MAC_PORT
read -r -p "Benutzername: " MAC_USER
ssh-keygen -t ed25519 -f "$HOME/.ssh/osxhost_build"
cat "$HOME/.ssh/osxhost_build.pub"
ssh -p "$MAC_PORT" "$MAC_USER@$MAC_HOST"

Die Datei mit dem öffentlichen Schlüssel endet auf .pub. Verbinden Sie sich zunächst mit den bereitgestellten Zugangsdaten und hängen Sie den öffentlichen Schlüssel an ~/.ssh/authorized_keys auf dem Mac an. Testen Sie anschließend die Anmeldung per Schlüssel in einem zweiten lokalen Terminal.

Öffentlichen Schlüssel hinterlegen, dann Passwort ändern

Legen Sie auf dem Mac ~/.ssh an und setzen Sie die Verzeichnisrechte auf 700, die Dateirechte des öffentlichen Schlüssels auf 600. Hängen Sie den Schlüssel mit einem Editor an und lassen Sie bestehende Einträge erhalten. Ändern Sie anschließend mit passwd das Passwort des aktuellen Benutzers.

Öffnen Sie ein weiteres Terminal und prüfen Sie die Anmeldung mit ssh -i ~/.ssh/osxhost_build -p "$MAC_PORT" "$MAC_USER@$MAC_HOST". Beenden Sie die ursprüngliche Sitzung erst, wenn die Anmeldung funktioniert. Schließen Sie nicht vorher die einzige funktionierende Verbindung.

Prüfung: Anmeldung per Schlüssel möglich

Bildschirmfreigabe über einen verschlüsselten Tunnel

Führen Sie den folgenden Tunnel-Befehl lokal aus und lassen Sie das Terminal geöffnet. Öffnen Sie unter macOS „Bildschirmfreigabe“ und verbinden Sie sich mit vnc://127.0.0.1:5901. Unter Windows und Linux verwenden Sie einen Client, der die jeweilige VNC-Authentifizierung unterstützt, und verbinden sich mit dem lokalen Port 5901.

Die Bildschirmfreigabe muss auf dem Mac aktiviert und für den aktuellen Benutzer freigegeben sein. Zugangsdaten für die grafische Anmeldung und der SSH-Schlüssel sind unterschiedliche Anmeldedaten. Geben Sie den VNC-Port nicht für alle Quellen frei.

Prüfung: macOS-Schreibtisch sichtbar und bedienbar
Lokaler SSH-Tunnel · Ziel ist die Loopback-Adresse des Macs
ssh -N -L 5901:127.0.0.1:5900 \
  -i "$HOME/.ssh/osxhost_build" \
  -p "$MAC_PORT" "$MAC_USER@$MAC_HOST"

Schritt 2 · Lokaler Mac → Cloud-Mac

Drei Wege zur Übernahme statt einer Kopie der alten Umgebung

Prüfen Sie zuerst Daten und Toolchain und registrieren Sie anschließend den CI-Runner. Kontrollieren Sie getrennt, ob die Anmeldung funktioniert und ob Builds unbeaufsichtigt laufen können. So wird eine lokale interaktive Konfiguration nicht mit einer Pipeline-Konfiguration verwechselt.

Weg 01 · Daten

Projekt übernehmen, Geheimnisse nicht im Klartext

  • Exportieren Sie benötigte Signaturzertifikate samt privatem Schlüssel aus dem Schlüsselbund als verschlüsselte .p12-Datei. Übermitteln Sie das Passwort über einen separaten sicheren Kanal.
  • Übernehmen Sie nur die für das aktuelle Projekt benötigten Provisioning-Profile und prüfen Sie Team, App-ID und Gültigkeit.
  • Kopieren Sie nicht den gesamten Anmelde-Schlüsselbund. Erstellen Sie einen separaten Build-Schlüsselbund, importieren Sie nur benötigte Elemente und autorisieren Sie das Signaturtool.
  • Synchronisieren Sie Quellcode und benötigte Ressourcen, aber keine Abhängigkeitsverzeichnisse, DerivedData oder alten Build-Artefakte.

Erfolgreich, wenn: Projektversion stimmt, Zertifikat den privaten Schlüssel enthält und keine sensiblen Dateien in Git liegen.

Weg 02 · Toolchain

Versionen festlegen, dann Abhängigkeiten installieren

  • Rufen Sie mit xcodes list die verfügbaren Versionen ab und installieren Sie nach den nötigen Freigaben die gewünschte Xcode-Version.
  • Legen Sie mit xcode-select die aktive Toolchain ausdrücklich fest, statt sich auf die zuletzt in der Oberfläche gewählte Version zu verlassen.
  • Prüfen Sie Homebrew und die arm64-Toolchain. Übernehmen Sie nicht einfach Abhängigkeitsverzeichnisse aus einer Intel-Umgebung.
  • Fixieren Sie Ruby, CocoaPods und fastlane mit einem Ruby-Versionsmanager und einem Gemfile.

Erfolgreich, wenn: Xcode-, Ruby- und Bundler-Versionen im Terminal und in CI übereinstimmen.

Weg 03 · CI-Anbindung

Runner nur vertrauenswürdige Jobs ausführen lassen

  • GitHub Actions: Registrieren Sie gemäß den Repository-Einstellungen einen self-hosted Runner und wählen Sie das macOS-ARM64-Paket.
  • GitLab Runner: Registrieren Sie einen Runner auf Projektebene, wählen Sie bei Bedarf die Shell-Ausführung und beschränken Sie ihn auf das Projekt.
  • Jenkins: Richten Sie einen macOS-Agent mit isoliertem Arbeitsverzeichnis und kontrollierter Übergabe von Zugangsdaten ein.
  • Führen Sie zunächst jeweils nur einen Signatur-Build aus und erhöhen Sie die Parallelität erst bei Bedarf und ausreichendem Arbeitsspeicher.

Erfolgreich, wenn: Ein Push mit einem vertrauenswürdigen Commit durchläuft und Logs sowie Build-Artefakte verfügbar sind.

Projekt lokal synchronisieren · vorhandene Dateien auf dem Mac behalten
rsync -avz \
  --exclude='.git/' \
  --exclude='build/' \
  --exclude='DerivedData/' \
  --exclude='Pods/' \
  --exclude='node_modules/' \
  -e "ssh -p $MAC_PORT -i $HOME/.ssh/osxhost_build" \
  ./ "$MAC_USER@$MAC_HOST:~/build-workspace/"

Dieser Synchronisierungsbefehl löscht keine Dateien auf dem Mac. Für produktive Pipelines sollten Sie einen festen Commit aus dem Repository auschecken. Der Cache-Schlüssel sollte mindestens Toolchain-Version, Lockfiles und Zielarchitektur enthalten. Lassen Sie nicht zu, dass nicht vertrauenswürdige externe Merge Requests auf Signaturzugangsdaten zugreifen.

Schritt 3 · Signatur und Artefakt getrennt prüfen

match, gym und pilot mit einem Fastfile verbinden

Führen Sie im Projektstamm zuerst bundle install aus und committen Sie Gemfile.lock. Der folgende Ablauf setzt voraus, dass das Signatur-Repository eingerichtet, die App angelegt und die Umgebungsvariablen sicher in CI hinterlegt sind. Neue Signaturressourcen werden damit nicht erstellt.

Vorabprüfung auf dem Mac · Toolchain kontrollieren
xcode-select -p
xcodebuild -version
xcodebuild -showsdks
ruby --version
bundle --version
bundle exec fastlane --version
security find-identity -v -p codesigning
df -h "$HOME"

Hinterlegen Sie die URL des Signatur-Repositorys in MATCH_GIT_URL und das Entschlüsselungspasswort in MATCH_PASSWORD. Für Uploads verwenden Sie einen serverseitigen API-Schlüssel: Der Pfad zur Schlüsseldatei kommt aus ASC_KEY_PATH, Schlüssel-ID und Issuer-ID aus ASC_KEY_ID beziehungsweise ASC_ISSUER_ID. Die Schlüsseldatei sollte zur Laufzeit aus dem Secret Store von CI bereitgestellt und nicht im Projekt-Repository abgelegt werden.

Setzen Sie IOS_APP_IDENTIFIER, IOS_TEAM_ID, IOS_WORKSPACE und IOS_SCHEME. Der Verteilungstyp im Signatur-Repository muss zu appstore passen. Prüfen Sie zuerst das Shared Scheme und starten Sie danach die vollständige Pipeline.

fastlane/Fastfile · TestFlight-Pipeline für eine App
default_platform(:ios)

platform :ios do
  desc "Archive a signed build and upload it to TestFlight"
  lane :release do
    setup_ci

    api_key = app_store_connect_api_key(
      key_id: ENV.fetch("ASC_KEY_ID"),
      issuer_id: ENV.fetch("ASC_ISSUER_ID"),
      key_filepath: ENV.fetch("ASC_KEY_PATH"),
      in_house: false
    )

    match(
      type: "appstore",
      readonly: true,
      git_url: ENV.fetch("MATCH_GIT_URL"),
      app_identifier: ENV.fetch("IOS_APP_IDENTIFIER"),
      team_id: ENV.fetch("IOS_TEAM_ID"),
      api_key: api_key
    )

    gym(
      workspace: ENV.fetch("IOS_WORKSPACE"),
      scheme: ENV.fetch("IOS_SCHEME"),
      configuration: "Release",
      clean: true,
      export_method: "app-store",
      output_directory: "build/artifacts",
      output_name: "release.ipa"
    )

    pilot(
      api_key: api_key,
      ipa: "build/artifacts/release.ipa",
      skip_waiting_for_build_processing: true
    )
  end
end
Ausführen und Ergebnis prüfen · CI setzt die Umgebungsvariablen
bundle exec fastlane ios release
find build/artifacts -maxdepth 1 -type f -print

Ein erfolgreicher Upload bedeutet nicht, dass die Verarbeitung abgeschlossen ist. Dieses Beispiel wartet nach dem Upload nicht auf die Verarbeitung. Prüfen Sie den Verarbeitungsstatus im Distributionsportal und verwenden Sie für jeden Upload eine eindeutige Build-Nummer. readonly: true verhindert Änderungen von CI am Signatur-Repository. Laufen Zertifikate oder Provisioning-Profile ab, aktualisieren Sie sie in einer kontrollierten Umgebung und führen Sie den Ablauf erneut aus.

Plattformübergreifende Projekte · Lockfiles beibehalten

iOS-Schritte separat ausführen und Caches gezielt erneuern

Die iOS-Builds von Flutter und React Native benötigen Xcode, CocoaPods und eine Signaturumgebung. Führen Sie zunächst die Plattformdiagnose aus und ergänzen Sie anschließend die Upload-Schritte. Überspringen Sie die Prüfung konsistenter Abhängigkeiten nicht, nur weil ein Cache-Treffer vorliegt.

Flutter: erst Simulator, dann IPA

Fixieren Sie die Flutter-SDK-Version und behalten Sie pubspec.lock bei. Verwenden Sie den Pub-Download-Cache weiter. Der Pods-Cache sollte auf Podfile.lock basieren, DerivedData auf Xcode-Version und Projektkonfiguration. Beim ersten Lauf ohne Cache dauern Downloads und Builds länger als bei späteren Durchläufen.

Führen Sie im Projektstamm die folgenden Befehle aus. Erst wenn die Signierung eingerichtet ist, kann flutter build ipa --release einen Build für echte Geräte exportieren.

Build-Caches nicht über Xcode-Hauptversionen hinweg teilen

React Native: Node, Ruby und Pods fixieren

Committen Sie das Lockfile Ihres JavaScript-Paketmanagers und verwenden Sie die im Projekt festgelegten Installationsbefehle. Das folgende Beispiel nutzt npm. Bundler verwaltet die Ruby-Abhängigkeiten, damit System-Gems und Projektversionen einander nicht überschreiben.

Verwenden Sie pod install --repo-update nur, wenn neue Abhängigkeiten tatsächlich eine Aktualisierung der Specs benötigen. Im Normalfall genügt bundle exec pod install. Löschen Sie nicht bei jedem Lauf Pods und Lockfiles.

Cache-Schlüssel mit Architektur und Lockfiles
Im Flutter-Projektstamm
flutter --version
flutter doctor -v
flutter pub get
flutter build ios --simulator
flutter build ipa --release
React-Native-Projektstamm · Projekte mit npm
node --version
npm ci
bundle install
(cd ios && bundle exec pod install)
xcrun simctl list devices available

Begrenzen Sie parallele Simulator-Tests. Ermitteln Sie zwei gültige UDIDs aus der Liste verfügbarer Geräte und setzen Sie sie als SIM_UDID_A und SIM_UDID_B. Führen Sie den folgenden Test in einem Projekt mit konfiguriertem IOS_WORKSPACE und IOS_SCHEME aus. Starten Sie zunächst mit einem Ziel und erhöhen Sie dann auf zwei. Behalten Sie Speicherauslastung und Festplattenzugriffe im Blick.

Xcode-Tests · zwei vom Projekt unterstützte Simulatorziele
xcodebuild test \
  -workspace "$IOS_WORKSPACE" \
  -scheme "$IOS_SCHEME" \
  -destination "platform=iOS Simulator,id=$SIM_UDID_A" \
  -destination "platform=iOS Simulator,id=$SIM_UDID_B" \
  -parallel-testing-enabled YES \
  -maximum-concurrent-test-simulator-destinations 2 \
  -resultBundlePath "build/TestResults-$(date +%s).xcresult"

Container-Grenzen · Linux-Dienste und macOS-Builds trennen

Container eignen sich für Zusatzdienste, nicht als Ersatz für einen Xcode-Host

Der Knoten selbst ist ein exklusiver physischer Mac. Linux-Container von Docker Desktop oder OrbStack laufen weiterhin in einer Linux-Umgebung. Der physische Mac bedeutet nicht, dass Container und macOS denselben Kernel verwenden.

AufgabeEmpfohlener OrtZu beachten
Testdatenbanken, Cache und interne APIsLinux-ContainerBevorzugen Sie arm64-Images und begrenzen Sie Arbeitsspeicher sowie Netzwerkzugriff.
Xcode-Archive und Simulator-TestsmacOS-HostBenötigen macOS-SDK, Signaturtools und Simulator-Runtime.
Dienste mit ausschließlich amd64-ImagesZuerst Kompatibilität prüfenEmulation kann zusätzlichen Aufwand verursachen. Rechnen Sie nicht mit der arm64-Leistung.
Dauerhaft laufende ZusatzdiensteNach Bedarf zur RessourcentrennungSie teilen physischen Arbeitsspeicher, SSD und Netzwerk mit Builds. Begrenzen Sie Ressourcen und halten Sie Backups vor.
Nach Installation und Start des Container-Tools prüfen
uname -m
docker version
docker info --format '{{.Architecture}}'
docker system df
docker stats --no-stream

Verwalten Sie Container entweder mit Docker Desktop oder OrbStack, damit klar bleibt, auf welchen Docker-Context Sie zugreifen. Notieren Sie vor dem Start zusätzlicher Dienste freien Arbeitsspeicher und Speicherplatz. Das Löschen von Containerdaten sollte nicht die Standardmaßnahme bei der Fehlersuche sein.

Erweiterungen · Gerät erst identifizieren, dann beschreiben

Caches auf ein Erweiterungslaufwerk verschieben, mehrere Macs nicht als einen behandeln

Eine zusätzliche SSD und ein Thunderbolt-5-Verbund sind optionale Konfigurationen. Maßgeblich sind die in den Bestelldaten angegebenen Gerätenamen, Mount-Pfade, Schnittstellen und Verbindungstopologien. Führen Sie Formatierungs- oder Partitionierungsbefehle erst aus, wenn Sie alles geprüft haben.

Zusätzliche SSD: Mountpoint und Rechte prüfen

Ermitteln Sie externe Geräte mit diskutil list external und prüfen Sie anschließend die Details des betreffenden Volumes. Ist das Volume formatiert, aber nicht eingebunden, prüfen Sie zuerst die Kennung und führen Sie dann diskutil mount "$DISK_IDENTIFIER" aus. Leiten Sie nicht allein aus der Laufwerksnummer ab, dass es sich um die zusätzliche SSD handelt.

Geeignet sind darauf etwa Abhängigkeits-Caches, DerivedData und temporäre Artefakte. Hinterlegen Sie den tatsächlichen Pfad in der CI-Variablen BUILD_CACHE_ROOT, prüfen Sie die Schreibrechte des Build-Benutzers und stellen Sie sicher, dass der Pfad nach einem Neustart noch verfügbar ist. Sichern Sie Quellcode und Signaturdaten separat.

Mehr Speicher ist kein Backup

TB5-Verbund: Verbindungstopologie für jeden Mac prüfen

Ein Verbund fasst weder die CPUs noch den einheitlichen Arbeitsspeicher mehrerer Macs automatisch zu einem Ressourcenpool für einen einzelnen Prozess zusammen. Aufgaben müssen von der Anwendung oder der CI-Steuerung verteilt werden. Toolchain, Runner und Arbeitsverzeichnis sind auf jedem Mac separat einzurichten.

Wenn die Bereitstellung eine Thunderbolt-Netzwerkverbindung umfasst, prüfen Sie für jeden Mac Netzwerkinterface, Adresse und Routing und testen Sie anschließend die direkte Verbindung. Geben Sie bei einer Anfrage die Anzahl der Geräte, das Modell und den Einsatzzweck an. Gehen Sie nicht davon aus, dass alle Modelle dieselben Schnittstellen unterstützen.

Verteilte Aufgaben müssen von der Anwendung unterstützt werden
Schreibgeschützte Prüfung auf dem Mac · ändert weder Laufwerke noch Netzwerk
diskutil list external
ls -la /Volumes
df -h
system_profiler SPThunderboltDataType
networksetup -listallhardwareports

Die vier Laufzeitpreise für Erweiterungen finden Sie auf der Tarifseite. Wenn Sie eine bestehende Bestellung ändern möchten, klären Sie Umsetzung und Datenmigration zuerst über ein Support-Ticket. Ändern Sie die Laufwerksstruktur nicht während eines produktiven Builds.

Letzter Schritt · Probleme mit konkreten Informationen eingrenzen

Erst die wichtigsten Diagnosedaten sammeln, dann Einstellungen ändern

Notieren Sie Bestellnummer, Knoten, Zeitpunkt samt Zeitzone und den Commit des letzten erfolgreichen Builds. Sichern Sie die erste Fehlermeldung und den Exit-Code, nicht nur die letzte Logzeile. Mit den folgenden Befehlen lässt sich die Ursache eingrenzen; sie beheben nicht automatisch jedes Problem.

01

SSH-Verbindung schlägt fehl

Prüfen Sie Adresse, Port, Benutzernamen und Pfad zum privaten Schlüssel. Bei einem Timeout prüfen Sie zuerst die Netzwerkverbindung. Bei fehlgeschlagener Authentifizierung kontrollieren Sie Benutzername, Freigabe des öffentlichen Schlüssels und Dateirechte. Lassen Sie einen geänderten Host-Fingerabdruck erst über ein Support-Ticket bestätigen, statt die Warnung zu ignorieren.

Lokales Terminal · Ausgabe kann Host-Informationen enthalten
ssh -vv -o ConnectTimeout=10 \
  -i "$HOME/.ssh/osxhost_build" \
  -p "$MAC_PORT" "$MAC_USER@$MAC_HOST"
Ticket zur Verbindung
02

Zertifikat abgelaufen oder Signaturidentität nicht gefunden

Prüfen Sie, ob das Zertifikat den privaten Schlüssel enthält und gültig ist und ob das Provisioning-Profil zu App und Team passt. Der CI-Schlüsselbund muss außerdem während des Build-Laufs verfügbar sein. Korrigieren Sie Signaturressourcen in einer kontrollierten Umgebung und synchronisieren Sie sie anschließend mit match im Nur-Lese-Modus.

Terminal auf dem Mac · Signaturidentitäten und Schlüsselbunde anzeigen
security find-identity -v -p codesigning
security list-keychains -d user
bundle exec fastlane match appstore --readonly
Ticket zur Signierung
03

Speicher voll, Archive kann nicht geschrieben werden

Unterscheiden Sie zuerst System- und Zusatzlaufwerk und ermitteln Sie den Platzbedarf von DerivedData, Simulatoren und Build-Artefakten. Löschen Sie nur Caches, die sich sicher neu erstellen lassen. Entfernen Sie nicht pauschal Schlüsselbunde, Signaturdateien oder aktive Arbeitsverzeichnisse.

Terminal auf dem Mac · Speicherbelegung anzeigen
df -h
du -sh "$HOME/Library/Developer/Xcode/DerivedData" \
  "$HOME/Library/Developer/CoreSimulator" \
  "$HOME/build-workspace" 2>/dev/null
xcrun simctl list devices unavailable
Ticket zum Speicherplatz
04

Xcode bleibt lange bei „Indexing“ stehen

Prüfen Sie, ob der Indexierungsprozess noch arbeitet, und kontrollieren Sie Installation der Abhängigkeiten und freien Speicherplatz. Die Indexierung in der grafischen Oberfläche und ein erfolgreicher Kommandozeilen-Build sind nicht dasselbe. Anhand der Logs lässt sich feststellen, ob das Archive tatsächlich blockiert ist.

Terminal auf dem Mac · Prozesse und Build-Konfiguration anzeigen
ps -axo pid,%cpu,%mem,etime,comm | grep -E 'Xcode|SourceKit|swift'
xcode-select -p
xcodebuild -list -workspace "$IOS_WORKSPACE"
df -h "$HOME"
Xcode-Ticket erstellen

Fügen Sie einem Ticket niemals private Schlüssel, Zertifikatspasswörter, Zugriffstoken oder den vollständigen Inhalt des Signatur-Repositorys bei. Schwärzen Sie vor dem Absenden Zugangsdaten und sensible Pfade in Logs. Wenn Sie sich nicht im Kundenportal anmelden können, schreiben Sie an support@osxhost.com und nennen Sie Bestellnummer und Fehlerbild. E-Mail und Support-Tickets im Kundenportal sind öffentliche Supportkanäle.

Erst ein Archive erfolgreich erstellen, dann parallelisieren

Halten Sie Toolchain-Version, Signaturübergabe, Cache-Schlüssel und Artefaktverzeichnis in der Pipeline fest. Wenn Sie einen Mac benötigen, bestellen Sie ihn. Wenn Sie nicht weiterkommen, reichen Sie ein Ticket mit den Diagnoseergebnissen ein.