Wenn mehrere Personen gleichzeitig an einem Xcode-Projekt arbeiten, sind Compilerfehler oft nicht das größte Risiko. Gefährlicher ist eine beschädigte project.pbxproj, die sich trotzdem ohne Textkonflikt zusammenführen lässt. Möglicherweise wurde eine Dateireferenz entfernt, ein Scheme nicht freigegeben oder eine wichtige Build-Einstellung unbemerkt über die Benutzeroberfläche geändert. Solche Probleme treten mitunter erst beim Archivieren zutage. Zuverlässiger ist es, die Projektdatei auf einem Cloud-Mac als eigenständige Build-Eingabe zu behandeln: Zuerst wird sie mit kostengünstigen Konsistenzprüfungen validiert, erst danach folgen Abhängigkeitsauflösung, Tests und Archivierung.
Was das Prüf-Gate kontrollieren muss
Ein praxistaugliches Gate sollte mindestens vier Ebenen abdecken, und zwar in dieser Reihenfolge: zuerst Textrückstände prüfen, dann das Dateiformat validieren, anschließend das Projekt tatsächlich von Xcode einlesen lassen und zuletzt die entscheidenden Einstellungen vergleichen. Schlägt eine Ebene fehl, sollte der Prozess sofort beendet werden, damit keine weitere Build-Zeit verbraucht wird.
| Ebene | Prüfobjekt | Bedeutung eines Fehlers |
|---|---|---|
| Text | Konfliktmarkierungen, leere Dateien | Der Merge ist noch nicht abgeschlossen |
| Struktur | project.pbxproj |
Die plist-Struktur kann nicht geparst werden |
| Projekt | Project, Target, Scheme | Xcode kann kein Projektmodell erstellen |
| Konfiguration | SDK, Deployment-Version, Signierungsmethode | Einstellungen sind unerwartet abgewichen |
Ein konfliktfreies
git diffbedeutet nicht, dass das Xcode-Projekt gültig ist. Die Versionsverwaltung bestätigt lediglich, dass der Text-Merge abgeschlossen wurde. Sie weiß nicht, ob Objektreferenzen, Targets oder Schemes weiterhin von Xcode erkannt werden.
Das Prüfskript sollte sowohl das Arbeitsverzeichnis als auch den Xcode-Pfad fest vorgeben und nicht vom aktuellen Zustand einer interaktiven Shell abhängen. Enthält das Projekt sowohl eine .xcodeproj- als auch eine .xcworkspace-Datei, sollte der reguläre Build den tatsächlich verwendeten Einstiegspunkt nutzen. Die zugrunde liegende project.pbxproj muss dennoch separat geprüft werden.
Merge-Rückstände und Formatfehler frühzeitig abfangen
Das folgende Skript kann als ci/check_xcode_project.sh gespeichert werden. Im Beispiel heißt das Projekt App.xcodeproj. In der Praxis sollte der Name über eine Umgebungsvariable überschrieben werden, damit er nicht über mehrere CI-Konfigurationen verteilt fest eingetragen werden muss.
#!/bin/bash
set -euo pipefail
PROJECT_PATH="${PROJECT_PATH:-App.xcodeproj}"
PBXPROJ="${PROJECT_PATH}/project.pbxproj"
test -s "$PBXPROJ" || {
echo "project.pbxproj is missing or empty"
exit 1
}
if grep -nE '^(<<<<<<<|=======|>>>>>>>)' "$PBXPROJ"; then
echo "merge conflict markers found"
exit 1
fi
plutil -lint "$PBXPROJ"
xcodebuild -list -json -project "$PROJECT_PATH" > /tmp/xcode-project-list.json
plutil -lint /tmp/xcode-project-list.json
Die Konfliktmarkierungen werden nur am Zeilenanfang gesucht. Dadurch entsteht kein Fehlalarm, wenn eine Folge von Gleichheitszeichen zufällig in einem fachlichen Dateinamen oder Kommentar vorkommt. Nach erfolgreichem plutil-Durchlauf wird xcodebuild -list aufgerufen, da erst dieser Befehl das Xcode-Projektmodell erstellt. Wird der Befehl mit einem Status ungleich null beendet, sollte die Standardfehlerausgabe erhalten bleiben. Das Projekt darf nicht automatisch „repariert“ oder neu generiert werden.
Umgang mit Workspace-Projekten
Bei Verwendung eines Workspace kommt eine zusätzliche Validierung des Einstiegspunkts hinzu:
WORKSPACE_PATH="${WORKSPACE_PATH:-App.xcworkspace}"
xcodebuild -list -json -workspace "$WORKSPACE_PATH" \
> /tmp/xcode-workspace-list.json
plutil -lint /tmp/xcode-workspace-list.json
Es reicht nicht, nur den Workspace zu validieren und die zugrunde liegenden Projekte zu überspringen. Ein erkannter Workspace garantiert nicht, dass jede darin enthaltene Projektdatei frei von Merge-Rückständen ist.
Freigegebene Schemes und die Zielmenge validieren
Jedes von der CI benötigte Scheme muss in der Versionsverwaltung liegen. Zunächst lässt sich die Datei des freigegebenen Schemes prüfen; anschließend wird der Name in der JSON-Ausgabe von xcodebuild -list bestätigt. Für die Auswertung genügt das im System enthaltene Python, zusätzliche Abhängigkeiten sind nicht erforderlich.
SCHEME_NAME="${SCHEME_NAME:-App}"
SCHEME_FILE="${PROJECT_PATH}/xcshareddata/xcschemes/${SCHEME_NAME}.xcscheme"
test -s "$SCHEME_FILE" || {
echo "shared scheme is missing: $SCHEME_NAME"
exit 1
}
python3 - "$SCHEME_NAME" /tmp/xcode-project-list.json <<'PY'
import json
import sys
expected = sys.argv[1]
path = sys.argv[2]
with open(path, encoding="utf-8") as handle:
payload = json.load(handle)
schemes = payload.get("project", {}).get("schemes", [])
if expected not in schemes:
raise SystemExit(f"expected scheme not found: {expected}")
PY
Enthält das Repository mehrere Apps oder Erweiterungen, sollte eine eindeutige Scheme-Liste gepflegt werden, statt lediglich „mindestens ein Scheme“ zu akzeptieren. Persönliche Schemes aus Benutzerverzeichnissen dürfen ebenfalls nicht als CI-Eingabe dienen. Nach dem Wechsel auf einen anderen physischen Knoten sind diese nicht freigegebenen Dateien dort nicht vorhanden.
Snapshot für wichtige Build-Einstellungen anlegen
Ein Projekt kann erfolgreich geparst werden, obwohl seine Konfiguration versehentlich verändert wurde. Für den Snapshot sollten nur Felder ausgewählt werden, die die Bedeutung des Build-Artefakts verändern, etwa PRODUCT_BUNDLE_IDENTIFIER, IPHONEOS_DEPLOYMENT_TARGET, SWIFT_VERSION, CODE_SIGN_STYLE und SUPPORTED_PLATFORMS. Die vollständige Ausgabe von -showBuildSettings sollte nicht gespeichert werden. Sie enthält Pfade und temporäre Verzeichnisse, die beim Vergleich verschiedener Knoten viel Rauschen verursachen.
xcodebuild -project "$PROJECT_PATH" \
-scheme "$SCHEME_NAME" \
-configuration Release \
-showBuildSettings |
awk -F ' = ' '
/PRODUCT_BUNDLE_IDENTIFIER =/ ||
/IPHONEOS_DEPLOYMENT_TARGET =/ ||
/SWIFT_VERSION =/ ||
/CODE_SIGN_STYLE =/ ||
/SUPPORTED_PLATFORMS =/ {
gsub(/^[ ]+/, "", $1)
print $1 " = " $2
}
' | LC_ALL=C sort > /tmp/build-settings.current
diff -u ci/build-settings.release /tmp/build-settings.current
Nach dem erstmaligen Erstellen muss ci/build-settings.release in die Versionsverwaltung aufgenommen werden. Soll die Deployment-Version oder die Signierungsmethode bewusst geändert werden, werden zunächst die Einstellungsunterschiede geprüft und anschließend der Snapshot im selben Änderungssatz aktualisiert. Die CI darf die Baseline niemals automatisch überschreiben, da sonst jede Abweichung zum neuen Standard würde.
In die Pipeline integrieren und Fehlalarme behandeln
Das Konsistenz-Gate sollte vor dem Herunterladen von Abhängigkeiten und vor dem vollständigen Build ausgeführt werden. Außerdem sollte das Skript klar erkennen lassen, in welcher Phase der Fehler aufgetreten ist. Die empfohlene Reihenfolge lautet: Code auschecken, eine feste Xcode-Version auswählen, das Projekt-Gate ausführen, Abhängigkeiten auflösen, kompilieren, testen und archivieren. Lokal und auf dem Cloud-Mac sollte dafür derselbe Einstiegspunkt verwendet werden, zum Beispiel:
DEVELOPER_DIR="/Applications/Xcode.app/Contents/Developer" \
PROJECT_PATH="App.xcodeproj" \
SCHEME_NAME="App" \
bash ci/check_xcode_project.sh
Typische Fehlalarme haben drei Ursachen. Erstens wurde ein Scheme geändert, aber nicht freigegeben. Die Lösung besteht darin, xcshareddata/xcschemes zu committen, nicht das Scheme manuell auf dem Knoten anzulegen. Zweitens enthält der Snapshot absolute Pfade; in diesem Fall muss die Feldmenge weiter eingeschränkt werden. Drittens verwenden verschiedene Jobs unterschiedliche Xcode-Pfade. Vor Beginn des Gates sollte deshalb die Ausgabe von xcodebuild -version protokolliert und geprüft sowie DEVELOPER_DIR festgelegt werden.
Bei einem Fehlschlag sollten das Ticket oder der Build-Datensatz mindestens die Commit-Kennung, die Xcode-Version, den fehlgeschlagenen Befehl, die Standardfehlerausgabe und den Projekteinstiegspunkt enthalten. Vollständige Umgebungsvariablen mit potenziell sensiblen Werten dürfen nicht hochgeladen werden. Ist ein Wechsel des physischen Knotens erforderlich, sollten die aktuell verfügbaren Konfigurationen in der Konsole geprüft werden. Der neue Knoten muss das Gate erneut aus demselben Repository ausführen, statt den temporären Projektzustand des alten Knotens zu übernehmen.
Checkliste vor dem Merge
Vor dem Commit des Gates ist Punkt für Punkt zu prüfen: Das Skript verwendet set -euo pipefail; Projektpfad und Scheme lassen sich über Umgebungsvariablen überschreiben; die Suche nach Konfliktmarkierungen scannt ausschließlich die Projektdatei; Project und Workspace werden entsprechend dem tatsächlichen Einstiegspunkt separat geparst; das freigegebene Scheme liegt in der Versionsverwaltung; der Einstellungs-Snapshot enthält nur stabile Felder; jede Aktualisierung der Baseline wurde manuell geprüft.
Diese Prüfungen ersetzen weder das Kompilieren noch die Tests. Sie verlagern jedoch eine Klasse von Fehlern, die sonst erst beim Archivieren sichtbar würde, in einen wenige Sekunden dauernden Schritt. Sobald die Projektdatei als explizite, überprüfbare und reproduzierbar verarbeitete Eingabe gilt, muss sich das Team zur Beurteilung des Hauptbranches nicht mehr darauf verlassen, dass „irgendein Entwickler das Projekt lokal noch öffnen kann“.
Häufig gestellte Fragen
Reicht plutil aus, um ein Xcode-Projekt vollständig zu prüfen?
Nein. plutil erkennt vor allem strukturelle und syntaktische Fehler. Zusätzlich sollte xcodebuild -list bestätigen, dass Xcode das Projekt öffnen kann und die erwarteten Schemes findet.
An welcher Stelle der CI-Pipeline sollte die Prüfung laufen?
Sie sollte vor dem vollständigen Build in jeder Änderung und erneut auf dem gemeinsamen Hauptzweig laufen. So werden defekte Projektdateien mit geringem Aufwand abgefangen.
Wie werden beabsichtigte Änderungen an Build-Einstellungen behandelt?
Das Team prüft die Differenz und aktualisiert anschließend bewusst den versionierten Referenzstand. Eine automatische Aktualisierung würde unbeabsichtigte Abweichungen verbergen.
Führe deinen nächsten Build auf einem dedizierten physischen Knoten aus.
Wähle Chip, Arbeitsspeicher, Speicher, Knoten und Laufzeit und erhalte einen Cloud-Mac mit Ressourcen, die nicht mit anderen Kunden geteilt werden.