Die Pipeline meldet nur „xcodebuild Exit Code 65“, obwohl der eigentliche Fehler früher im Log steht.
Schnellste Lösung: Sichern Sie zuerst den vollständigen Befehl, das gesamte Log und die .xcresult-Datei. Ermitteln Sie danach die erste fehlgeschlagene Aktion und prüfen Sie in dieser Reihenfolge Scheme, Abhängigkeiten, Zielgerät, Signierung und Knotenstatus. Eine allgemeine Reparatur durch Cache-Löschen gibt es nicht.
Zeitplan und Aktion für diese Woche
| Meilenstein | Was Sie sichern oder prüfen | Ergebnis |
|---|---|---|
| Triage | Vollständiger xcodebuild-Befehl, Xcode-Pfad, Benutzerkonto und Startzeit |
Die Ausführung ist reproduzierbar dokumentiert |
| Fehlergrenze | Erste fehlgeschlagene Aktion im Log und zugehöriger Abschnitt in xcresult |
Kettenfehler werden von der Ursache getrennt |
| Minimaltest | Derselbe Quellstand mit reduziertem Befehl und identischem Ziel | Projektfehler und CI-Umgebung lassen sich unterscheiden |
| Reparaturtest | Behobener Befehl, danach Wiederholung ohne weitere Änderung | Die Korrektur ist nachweisbar |
| Knotenentscheidung | Vergleich vor und nach Neustart sowie mit sauberem Arbeitsverzeichnis | Reparatur, Isolation oder Neuaufbau wird begründet |
Diese Woche sollten Sie nicht mit einem neuen Runner oder dem Löschen von DerivedData beginnen. Legen Sie zunächst ein unverändertes Beweisarchiv an. Dazu gehören der Commit, der vollständige Aufruf, die Ausgabe von xcode-select, der Wert von DEVELOPER_DIR, der ausführende Benutzer und die .xcresult-Datei. Erst wenn diese Daten vorliegen, ist eine Änderung an Projekt oder Mac-Knoten sinnvoll.
Diese Anleitung ist für Sie relevant, wenn der Build in Xcode lokal erfolgreich ist, das Remote Mac CI jedoch mit Exit Code 65 endet. Sie verwalten möglicherweise gemeinsam genutzte macOS-Buildknoten und müssen eine einheitliche Xcode-Toolchain sicherstellen. Oder Sie verantworten Releases und müssen sauber zwischen Kompilierung, Tests, Signierung und Archivierung unterscheiden.
Exit Code 65 ist ein Endstatus, keine Fehlerdiagnose
xcodebuild meldet mit Exit Code 65 lediglich, dass die angeforderte Aktion fehlgeschlagen ist. Der Wert sagt nicht, ob ein Scheme falsch gewählt wurde, eine Swift-Package-Abhängigkeit fehlt, ein Simulator nicht verfügbar ist oder die Signierung scheitert. Die konkrete Ursache muss aus der vorherigen Ausgabe und dem Testergebnis rekonstruiert werden.
Apple beschreibt in der technischen Referenz zu xcodebuild, wie Projekte, Workspaces, Schemes, Ziele und Testergebnisse über die Kommandozeile angesprochen werden. Für die Diagnose bedeutet das: Der Befehl selbst ist Teil des Fehlers. Ein fehlender Parameter oder ein anderer Ausführungspfad kann den scheinbaren Unterschied zwischen lokalem Xcode und CI erklären.
Warum funktioniert der Build lokal, aber nicht im CI?
Lokales Xcode verwendet häufig ein anderes Benutzerkonto, eine andere Schlüsselbundsitzung, einen anderen Simulator, bereits geladene Paketabhängigkeiten oder ein anderes ausgewähltes Entwicklerverzeichnis. Außerdem kann eine grafische Sitzung Zugang zu privaten Schlüsseln haben, den ein unbeaufsichtigter CI-Prozess nicht besitzt. „Lokal erfolgreich“ beweist daher nur, dass genau diese lokale Kombination funktioniert.
Suchen Sie im Log nicht nach der letzten roten Zeile, sondern nach dem ersten Fehler, der eine Aktion tatsächlich beendet. Spätere Meldungen wie „Testing failed“, „Archive failed“ oder ein allgemeiner CI-Wrapper sind häufig nur Folgewirkungen. Der erste relevante Fehler enthält typischerweise den Namen des Ziels, der Datei, des Pakets, des Simulators oder der Signaturressource.
Sichern Sie mindestens:
- den vollständigen
xcodebuild-Aufruf einschließlich aller-destination-,-scheme- und-configuration-Werte; - den ausgewählten Xcode-Pfad und die verwendete Kommandozeilenwerkzeug-Auswahl;
- das ausführende Benutzerkonto und dessen Arbeitsverzeichnis;
- das vollständige Standard- und Fehlerlog;
- die unveränderte
.xcresult-Datei.
Die Apple-Dokumentation zu Testergebnissen beschreibt, wie Testläufe und deren Resultate ausgewertet werden. Öffnen oder exportieren Sie das Ergebnis erst nach der Sicherung der Originaldatei. So bleibt nachvollziehbar, ob der Fehler beim Bauen, beim Start des Testziels oder innerhalb eines Tests auftrat.
Projektparameter gegen Umgebungsannahmen abgleichen
Ein sehr häufiger Unterschied liegt nicht im Quellcode, sondern im tatsächlich verwendeten Projektmodell. Prüfen Sie, ob die Pipeline ein .xcodeproj oder ein .xcworkspace baut. Wird ein Workspace mit Pods oder Swift Packages erwartet, kann ein direkter Projektaufruf Abhängigkeiten und Build-Phasen auslassen.
Listen Sie die verfügbaren Schemes auf und vergleichen Sie die Ausgabe mit dem lokalen Aufruf:
xcodebuild -list -workspace "App.xcworkspace"
xcodebuild -showBuildSettings \
-workspace "App.xcworkspace" \
-scheme "APP_SCHEME"
Verwenden Sie ausschließlich Platzhalter wie APP_SCHEME, App.xcworkspace und TEST_DEVICE_ID in Dokumentation und Skripten. Ein Scheme muss für die Kommandozeile verfügbar sein; ein nur lokal vorhandenes oder nicht gemeinsam genutztes Scheme ist auf dem CI-Knoten nicht automatisch nutzbar.
Vergleichen Sie anschließend diese Parameter:
-workspaceoder-project;-scheme;-configuration;-sdk;-destination;- zusätzliche Build Settings;
- Export- und Archivparameter.
Die Reihenfolge der Build-Settings ist entscheidend. Apple erklärt in der Dokumentation zur Konfiguration von Build Settings, dass Werte aus höher priorisierten Ebenen niedrigere Projektwerte überschreiben können. Ein CI-Skript kann deshalb unbemerkt eine Einstellung ersetzen, die in Xcode korrekt aussieht. Durchsuchen Sie die Pipeline nach KEY=VALUE, xcodebuild -key value und Umgebungsvariablen.
Muss DerivedData sofort gelöscht werden?
Nein. Löschen Sie DerivedData nur, wenn die Beweislage auf veraltete Buildartefakte oder eine beschädigte inkrementelle Ausgabe zeigt. Das Löschen beseitigt zugleich nützliche Vergleichsdaten und kann den nächsten Lauf deutlich verändern. Führen Sie zuerst einen Minimaltest mit einem isolierten Ausgabeordner aus, zum Beispiel:
xcodebuild \
-workspace "App.xcworkspace" \
-scheme "APP_SCHEME" \
-destination "platform=iOS Simulator,id=TEST_DEVICE_ID" \
-derivedDataPath "/tmp/ci-derived-data" \
build
Wenn dieser Lauf mit demselben Fehler endet, war der globale Cache wahrscheinlich nicht die Hauptursache. Wenn er nur im alten Arbeitsverzeichnis scheitert, sichern Sie das Verzeichnis und dokumentieren Sie die Bereinigung, bevor Sie es entfernen.
Abhängigkeiten und Skripte getrennt von Xcode-Fehlern prüfen
Swift Packages können auf dem entfernten Mac anders aufgelöst werden als auf dem Entwicklergerät. Vergleichen Sie Package.resolved mit dem Commit, der in CI ausgecheckt wird. Prüfen Sie außerdem, ob der CI-Benutzer private Repositories erreichen kann und ob seine SSH-Konfiguration den erwarteten Hostschlüssel kennt.
| Beobachtung im Log | Wahrscheinliche Fehlergrenze | Nächste Prüfung |
|---|---|---|
| Auflösung eines privaten Pakets bricht vor dem Build ab | Zugangsdaten, SSH oder Netzwerk | Package.resolved, Repository-Zugriff, known_hosts und Benutzerkonto |
| Ein Run Script endet mit einem eigenen Fehlerstatus | Skript, Arbeitsverzeichnis oder Eingabedatei | Shell, Pfade, erzeugte Dateien und Umgebungsvariablen |
| Compiler meldet fehlendes Modul nach erfolgreicher Paketphase | Build-Settings oder Zielzuordnung | Scheme, Target-Mitgliedschaft und Suchpfade |
| Abhängigkeiten werden bei jedem Lauf neu geladen | Fehlende Reproduzierbarkeit oder falscher Cache | Gesperrte Versionen und kontrollierte Cache-Strategie |
| Fehler erscheint nur bei unbeaufsichtigter Sitzung | Rechte oder nicht vererbte Umgebung | Ausführender Benutzer und interaktive Shell-Annahmen |
Apple weist in der Anleitung für Swift Packages und CI-Workflows auf reproduzierbare Abhängigkeiten und die Grenzen der Systemwerkzeuge hin. Verwenden Sie die dort beschriebenen Prinzipien, statt im CI stillschweigend eine andere Paketversion zu akzeptieren.
Run Script Phases verdienen eine eigene Prüfung. Ein Skript kann erfolgreich kompilierte Quellen vorfinden und dennoch mit einem falschen Arbeitsverzeichnis, einer fehlenden Eingabedatei oder einer nicht gesetzten Variable abbrechen. Protokollieren Sie deshalb den Startordner, den Shell-Pfad und die erwarteten Eingabedateien innerhalb des Skripts. Prüfen Sie den Exit-Status des Skripts separat vom Compilerstatus.
Wie finden Sie die echte Ursache aus einem langen xcodebuild-Log?
Markieren Sie zunächst die erste Zeile mit error: oder einem expliziten Abbruch. Ordnen Sie sie anschließend der laufenden Phase zu: Paketauflösung, Skript, Kompilierung, Linker, Teststart, Testausführung, Archivierung oder Export. Bestätigen Sie die Zuordnung in xcresult. Ein späterer Exit Code 65 darf erst dann als Root Cause dokumentiert werden, wenn kein früherer konkreter Fehler existiert.
Simulator und Testziel nicht mit erfolgreicher Kompilierung verwechseln
Ein Build kann erfolgreich erstellt werden, während der Testlauf nicht startet. Das passiert beispielsweise, wenn das angeforderte Zielgerät nicht vorhanden ist, der benötigte Simulatorruntime fehlt oder das Scheme das Testziel für die gewählte Plattform nicht unterstützt. Der Simulator muss daher nicht nur starten; er muss zum Scheme, zur Architektur und zum konkreten -destination passen.
Prüfen Sie den Zielausdruck gegen die auf dem Knoten verfügbaren Ziele. Verwenden Sie in der Pipeline einen festen, dokumentierten Platzhalter für die Gerätekennung und ersetzen Sie ihn nur durch eine tatsächlich vorhandene Kennung. Ein frei formulierter Name kann auf einem anderen Knoten zu einem anderen Ziel aufgelöst werden oder gar nicht verfügbar sein.
Die Apple-Unterlagen zur Testausführung und Ergebnisinterpretation helfen dabei, die Testsession, das Zielgerät und die Logabschnitte in xcresult voneinander zu trennen. Prüfen Sie, ob der Fehler beim Erstellen des Testbinaries, beim Booten des Simulators, beim Installieren der App oder erst im Testprozess auftritt.
Warum kann ein Simulatorfehler ebenfalls Exit Code 65 auslösen?
Weil xcodebuild die gesamte angeforderte Aktion bewertet. Wenn der Build fertig ist, das Testziel aber nicht gestartet oder installiert werden kann, schlägt die Testaktion trotzdem fehl. Der Exitstatus beschreibt dann die fehlgeschlagene Gesamtaktion, nicht ausschließlich den Compiler.
Nach jeder Reparatur müssen Sie exakt denselben Destination-Ausdruck erneut verwenden. Ein Wechsel auf ein anderes Gerät oder eine andere Plattform kann den Fehler nur verdecken. Erst wenn der identische Testlauf wiederholt erfolgreich ist, dürfen Sie die Änderung als bestätigt betrachten.
Signierung erst nach dem ersten Signaturfehler anfassen
Entfernen Sie Zertifikate oder deaktivieren Sie die Signierung nicht als reflexartige Reaktion auf Exit Code 65. Prüfen Sie Signierung erst dann, wenn der erste konkrete Fehler auf Team-ID, Zertifikat, privaten Schlüssel, Provisioning Profile, Schlüsselbund oder Code Signing verweist.
Vergleichen Sie zwischen lokaler und entfernter Ausführung:
- Team-ID und Bundle-Identifier;
- verwendete Signaturmethode;
- verfügbare Zertifikate einschließlich privater Schlüssel;
- Provisioning Profile;
- Schlüsselbundpfad und Zugriff des CI-Benutzers;
- Archiv- und Exportparameter.
Ein grafisches Xcode-Fenster kann auf einen Schlüsselbund zugreifen, den ein SSH-Prozess oder ein Launch-Agent nicht sieht. Apple beschreibt in den Hinweisen zur Automatisierung von Tests über SSH, dass eine entfernte Sitzung eigene Umgebungs- und Sitzungsbedingungen mitbringt. Behandeln Sie deshalb die Identität des CI-Benutzers als Teil der Buildkonfiguration.
Für Archive trennen Sie die Nachweise:
- Bauen Sie mit der vorgesehenen Release-Konfiguration.
- Archivieren Sie mit demselben Quellstand und derselben Signaturkonfiguration.
- Exportieren Sie das Archiv in einem separaten Schritt.
- Sichern Sie Log und Ergebnisdatei jedes Schritts getrennt.
- Wiederholen Sie den fehlgeschlagenen Schritt ohne weitere Änderung.
Die Apple-Dokumentation zu Build Settings ist besonders relevant, wenn das CI-Skript Signaturwerte überschreibt. Eine Änderung an CODE_SIGN_STYLE, Team-ID oder Profilzuordnung muss im Log sichtbar und reproduzierbar sein. Das Abschalten der Signierung kann für einen reinen Kompilierungstest zulässig sein, ist aber keine allgemeine Lösung für ein veröffentlichungsfähiges Archiv.
Remote-Mac-Knoten mit einer Beweismatrix bewerten
Wenn Projekt, Abhängigkeiten, Ziel und Signierung plausibel sind, untersuchen Sie den Knoten selbst. Prüfen Sie den aktuell ausgewählten Xcode, DEVELOPER_DIR, Benutzerrechte, freien Speicher, verbliebene Arbeitsverzeichnisse und Unterschiede nach einer Sitzung oder einem Neustart. Die Apple-Anleitung zu den Kommandozeilenwerkzeugen erklärt, wie die Auswahl dieser Werkzeuge konfiguriert wird.
Legen Sie für jeden Vorfall diese Matrix an:
| Lauf | Quellstand und Befehl | Knotenbedingung | Beleg | Entscheidung |
|---|---|---|---|---|
| A | Originalbefehl | unverändert | vollständiges Log und xcresult |
Ausgangsfehler |
| B | Minimalbefehl | gleiches Arbeitsverzeichnis | reduziertes Log | Projekt oder Umgebung |
| C | Behobener Befehl | gleicher Knoten | wiederholtes Ergebnis | Reparatur bestätigt |
| D | Behobener Befehl | nach Neustart | neues Log und xcresult |
Sitzungs- oder Knotenproblem |
| E | Behobener Befehl | sauberes Arbeitsverzeichnis | Vergleichsergebnis | Restzustand ausgeschlossen |
Wann reparieren Sie, wann isolieren oder ersetzen Sie den Knoten?
Wenn derselbe Fehler mit identischer Konfiguration auf jedem verfügbaren Knoten auftritt, reparieren Sie zuerst Projekt, Abhängigkeit, Ziel oder Signierung. Wenn nur ein Knoten betroffen ist und der Minimaltest auf einem sauberen Knoten funktioniert, isolieren Sie den betroffenen Knoten und sammeln Sie seine Umgebungsdaten.
Wenn der Fehler nach einem Neustart verschwindet, ist das noch kein stabiler Nachweis. Wiederholen Sie den Lauf nach der normalen Wiederaufnahme des CI-Dienstes. Bleibt nur ein einzelner Knoten nicht reproduzierbar, ist eine kontrollierte Neuinstallation oder ein Austausch begründbar. Ein Neuaufbau sollte jedoch die letzte Stufe sein, weil er die ursprünglichen Beweise zerstört und möglicherweise nur eine nicht dokumentierte Abweichung entfernt.
Für eine solche Gegenprobe kann ein vollständig berechtigter, zurücksetzbarer Mac sinnvoll sein. VMSPIN beschreibt auf seiner deutschen Übersichtsseite für Remote-Mac-Zugriff die Nutzung einer entfernten macOS-Umgebung. Prüfen Sie vor der Migration, ob Sie denselben Quellstand, dieselben Befehle und dieselben Zugangsvoraussetzungen abbilden können. Eine andere Maschine ist nur dann ein Test, wenn der Vergleich kontrolliert bleibt.
Die Reparaturentscheidung als klare Verzweigung
Verwenden Sie nach der ersten Beweissicherung diese Bedingungen:
- Wenn der erste Fehler ein unbekanntes oder nicht geteiltes Scheme nennt, dann korrigieren Sie Workspace, Scheme oder Freigabe; sonst gehen Sie zur Abhängigkeitsprüfung.
- Wenn die Paketauflösung oder ein privates Repository zuerst fehlschlägt, dann reparieren Sie Lockdatei, SSH-Zugang,
known_hostsoder Benutzerrechte; sonst prüfen Sie Run Script und Compiler. - Wenn der Build erfolgreich ist, aber
xcresulteinen fehlgeschlagenen Teststart zeigt, dann gleichen Sie Runtime, Zielgerät und Scheme ab; sonst prüfen Sie die nächste Phase. - Wenn der erste konkrete Fehler auf Signierung verweist, dann vergleichen Sie Identitäten, Schlüsselbund und Profile für den CI-Benutzer; sonst ändern Sie keine Zertifikate.
- Wenn ein sauberer Lauf auf einem anderen Knoten funktioniert, dann isolieren Sie den ursprünglichen Knoten; sonst bleibt die Projekt- oder Pipelinekonfiguration der Hauptverdächtige.
- Wenn der Fehler nur nach Sitzungswechsel oder Neustart verschwindet, dann führen Sie Wiederholungstests durch; sonst dürfen Sie keine dauerhafte Reparatur behaupten.
- Wenn nur eine belegte, instabile Knoteninstallation betroffen ist, dann planen Sie einen kontrollierten Neuaufbau; sonst ist ein Neuaufbau voreilig.
Diese Regeln verhindern, dass Sie aus einem einmaligen grünen Lauf eine falsche Ursache ableiten. Besonders bei gemeinsam genutzten Remote Mac CI-Knoten müssen Sie Arbeitsverzeichnis, Benutzerkonto und Xcode-Auswahl als veränderliche Zustände behandeln.
Aktueller CI-Weg oder gemieteter Mac?
Wenn Ihre bisherige Lösung auf einem gemeinsam genutzten Runner, einer lokalen Entwickler-Maschine oder einem ungeprüften virtuellen macOS-Setup basiert, entstehen drei konkrete Nachteile: Die Xcode-Auswahl kann zwischen Jobs abweichen, private Schlüssel sind in unbeaufsichtigten Sitzungen schwer kontrollierbar, und ein beschädigter Arbeitszustand kann den nächsten Lauf beeinflussen. Ein Linux-Server beseitigt diese Punkte nicht, wenn Sie Xcode, Apple-SDKs oder echte macOS-Signierung benötigen.
Nach der Minimalreproduktion kann deshalb ein gemieteter Mac die bessere Testfläche sein: vollständige Rechte erleichtern die Prüfung von Benutzerkonto, Schlüsselbund und Werkzeugauswahl; ein zurücksetzbarer Knoten trennt Projektfehler von Altlasten; und Sie müssen für einen zeitlich begrenzten CI-Versuch keine eigene Hardware anschaffen. Die passende Mietoption und Laufzeitübersicht sollten Sie jedoch erst nach der Ursachenanalyse bewerten. Für dauerhaft hohe Last, spezielle physische Schnittstellen oder langfristig planbare Hardwarekosten kann ein eigener Mac weiterhin sinnvoller sein.
Wenn der Fehler auf einer kontrollierten VMSPIN-Umgebung mit demselben Repository und Befehl verschwindet, vergleichen Sie anschließend Laufzeit, Isolation und Wiederanlaufverhalten. Falls Sie diese Gegenprobe direkt durchführen möchten, finden Sie den Zugang zu einer gemieteten Mac-Umgebung. Entscheidend bleibt: Mieten Sie nicht, um Exit Code 65 zu überdecken, sondern um nach der Beweissicherung eine reproduzierbare Vergleichsumgebung für Ihr CI zu erhalten.
Ein belastbarer Abschluss besteht aus vier Nachweisen: ursprünglicher Fehler, minimaler Vergleich, korrigierter Wiederholungslauf und Verhalten nach Neustart. Erst dann ist klar, ob Sie eine Buildkonfiguration ändern, eine Abhängigkeit reparieren, einen Knoten isolieren oder die Umgebung neu aufbauen müssen.