Ein Storyboard kann sich auf dem Entwicklungsrechner problemlos öffnen lassen und nach einem Merge dennoch dazu führen, dass ein entfernter Archivierungsauftrag erst nach der Hälfte der Laufzeit fehlschlägt. Abhängigkeiten erneut herunterzuladen, einen Simulator zu starten oder DerivedData zu löschen, hilft in diesem Fall nicht. Die eigentliche Ursache kann ein ungültiges Outlet, ein falsches Modul für eine benutzerdefinierte Klasse oder eine XIB-Eigenschaft sein, die vom aktuellen Deployment-Target nicht unterstützt wird. Für Continuous Integration auf einem Cloud-Mac ist es effizienter, die UI-Ressourcen zunächst separat mit ibtool zu kompilieren und erst danach zu entscheiden, ob der vollständige Build ausgeführt werden soll.
UI-Ressourcen vor dem Build prüfen
ibtool gehört zu Xcode und sollte über xcrun aufgerufen werden, anstatt den Werkzeugpfad im Skript fest einzutragen. Das Werkzeug kann Storyboards und XIB-Dateien einlesen, errors, warnings und notices ausgeben sowie die Quelldateien in storyboardc- oder nib-Ausgaben kompilieren. Für diesen Schritt muss kein Simulator gestartet werden. Er eignet sich daher für die Ausführung nach dem Auflösen der Abhängigkeiten und vor xcodebuild archive.
Prüfen Sie zunächst, welches Entwicklerverzeichnis der Runner tatsächlich verwendet:
set -euo pipefail
xcode-select -p
xcrun --find ibtool
xcodebuild -version
xcrun ibtool --version
Diese Angaben sollten im Auftragsprotokoll erscheinen. Wenn das Team mehrere Xcode-Versionen auf demselben Cloud-Mac vorhält, muss die Pipeline DEVELOPER_DIR explizit setzen und die Umgebungsvariable nach Abschluss des Auftrags wieder entfernen. Andernfalls können nachfolgende Aufträge unbeabsichtigt die falsche Toolchain übernehmen.
Dass sich eine UI-Datei im grafischen Editor öffnen lässt, bedeutet nicht, dass sie mit der angegebenen Xcode-Version, dem Deployment-Target und dem jeweiligen Modulkontext erfolgreich kompiliert werden kann. Die CI-Prüfung soll die Release-Toolchain reproduzieren, nicht die Desktopumgebung eines einzelnen Entwicklers.
Storyboards und XIB-Dateien scannen und separat kompilieren
Das folgende Skript durchsucht das Repository nach UI-Dateien und legt für jede Eingabedatei ein separates Ausgabeverzeichnis an. Build- und Abhängigkeitsverzeichnisse müssen ausgeschlossen werden, da sonst generierte Dateien oder Ressourcen von Drittanbietern möglicherweise mehrfach geprüft werden.
#!/usr/bin/env bash
set -euo pipefail
root="${1:-$PWD}"
target="${IPHONEOS_DEPLOYMENT_TARGET:-16.0}"
work="${TMPDIR:-/tmp}/ibtool-preflight"
log="$work/ibtool.log"
rm -rf "$work"
mkdir -p "$work/out"
: > "$log"
find "$root" \
\( -path "*/DerivedData/*" -o -path "*/build/*" -o -path "*/Pods/*" \) -prune \
-o \( -name "*.storyboard" -o -name "*.xib" \) -print0 |
while IFS= read -r -d '' source; do
relative="${source#"$root"/}"
safe_name="$(printf '%s' "$relative" | tr '/ ' '__')"
case "$source" in
*.storyboard)
output="$work/out/${safe_name%.storyboard}.storyboardc"
;;
*.xib)
output="$work/out/${safe_name%.xib}.nib"
;;
esac
printf 'Checking %s
' "$relative" | tee -a "$log"
xcrun ibtool \
--errors \
--warnings \
--notices \
--minimum-deployment-target "$target" \
--compile "$output" "$source" 2>&1 | tee -a "$log"
done
Das Skript verwendet ein temporäres Verzeichnis und verändert das Repository nicht. Bei einem Fehler sollte ibtool.log aufbewahrt werden. Bei erfolgreicher Prüfung können die kompilierten Ergebnisse gelöscht und nur eine Zusammenfassung des Protokolls hochgeladen werden. Temporäre Ausgaben gehören nicht in die Versionsverwaltung. Mehrere parallele Aufträge dürfen außerdem kein festes Verzeichnis gemeinsam verwenden.
Grenzen zwischen separater Vorabprüfung und Projekt-Build verstehen
Ein separater Aufruf von ibtool kann beschädigtes XML, bestimmte fehlerhafte Verbindungen, inkompatible Eigenschaften und Kompilierungsfehler erkennen. Das Werkzeug kennt jedoch nicht alle Build-Einstellungen des vollständigen Targets. Zu welchem Modul ein benutzerdefinierter View-Controller gehört, ob eine Ressource in Copy Bundle Resources enthalten ist und ob eine bedingt kompilierte Klasse existiert, muss weiterhin mit xcodebuild geprüft werden.
Die Prüfung sollte aus zwei Ebenen bestehen:
| Ebene | Eingabe | Wichtigste Funde | Behandlung bei Fehlern |
|---|---|---|---|
| Schnelle Vorabprüfung | Storyboard, XIB | Dateibeschädigungen, inkompatible Eigenschaften, grundlegende Verbindungsfehler | Sofort abbrechen |
| Projekt-Build | workspace, project, Scheme | Modulauflösung, Ressourcenzuordnung, Verknüpfungs- und Signierungskontext | Vollständiges Build-Protokoll aufbewahren |
Wenn die separate Vorabprüfung erfolgreich ist, der Projekt-Build aber fehlschlägt, sollten zuerst Target Membership, das Feld Module, der Verlauf von Klassenumbenennungen und die Vererbung des Deployment-Targets geprüft werden. Schlägt dagegen bereits die Vorabprüfung fehl, muss die zeitaufwendigere Archivierung nicht mehr ausgeführt werden.
Lokalisierungsdateien in dasselbe Quality Gate aufnehmen
Für die Lokalisierung von Storyboards sind zwei Varianten üblich: separate Ressourcen für jede Sprache oder Base Internationalization in Verbindung mit .strings-Dateien. Bei der ersten Variante können sich Objekt-IDs auseinanderentwickeln. Bei der zweiten bleiben leicht Schlüssel für bereits gelöschte Objekte zurück. Prüfen Sie zunächst mit plutil das grundlegende Format der .strings-Dateien:
find . -name "*.strings" -print0 |
while IFS= read -r -d '' file; do
plutil -lint "$file"
done
Für ein Base Storyboard lässt sich die aktuelle Schlüsselmenge generieren und anschließend mit den Lokalisierungsdateien im Repository vergleichen:
mkdir -p "${TMPDIR:-/tmp}/ibtool-strings"
xcrun ibtool \
--generate-strings-file "${TMPDIR:-/tmp}/ibtool-strings/Main.strings" \
"App/Base.lproj/Main.storyboard"
plutil -lint "${TMPDIR:-/tmp}/ibtool-strings/Main.strings"
Die generierte Datei dient ausschließlich dem Vergleich und darf vorhandene Übersetzungen nicht direkt überschreiben. Ein robusterer Ablauf extrahiert und sortiert die Schlüsselnamen, bevor die Unterschiede ermittelt werden: Neue Schlüssel kommen auf die Übersetzungsliste, gelöschte Schlüssel erzeugen einen Bereinigungshinweis, und die Texte vorhandener Schlüssel werden weiterhin über den Lokalisierungsprozess gepflegt. So werden fehlende Einträge erkannt, ohne bestehende Übersetzungen versehentlich durch den Base-Text zu ersetzen.
Warnungs-Baselines und häufige Fehlalarme behandeln
Sämtliche Ausgaben als Fehler zu behandeln, wirkt kurzfristig streng, führt in der Praxis aber dazu, dass das Team rote Builds ignoriert. Eine besser umsetzbare Regel lautet: errors führen sofort zum Abbruch; warnings werden mit einer geprüften Baseline im Repository verglichen, wobei nur neue Einträge blockieren; notices werden in den Build-Artefakten gespeichert und regelmäßig bereinigt.
Gehen Sie bei der Fehlersuche in dieser Reihenfolge vor:
- Vergewissern Sie sich, dass
DEVELOPER_DIRmit dem vollständigen Build übereinstimmt. - Prüfen Sie, ob
IPHONEOS_DEPLOYMENT_TARGETaus den tatsächlichen Projekteinstellungen stammt. - Kontrollieren Sie die Groß- und Kleinschreibung von Dateipfaden, insbesondere bei Ressourcen, deren Umbenennung nur die Schreibweise geändert hat.
- Suchen Sie nach ungültigen Outlets, Actions und Namen benutzerdefinierter Klassen.
- Stellen Sie sicher, dass temporäre Verzeichnisse pro Auftrag isoliert sind, damit parallele Kompilierungen ihre Ausgaben nicht gegenseitig überschreiben.
- Führen Sie mit demselben Scheme einen weiteren Build ohne Signierung aus, um den Modulkontext zu prüfen.
Die Warnungs-Baseline sollte stabile Kennungen statt temporärer absoluter Pfade enthalten. Führen Sie die Vorabprüfung bei einem Xcode-Upgrade zunächst in einem separaten Branch aus, prüfen Sie die neuen Diagnosen und aktualisieren Sie erst danach die Baseline. Filtern Sie in einem Upgrade-Commit nicht einfach alle neuen Warnungen heraus.
Eine rücksetzbare Einführungsreihenfolge festlegen
Bei der ersten Integration können Sie eine Woche lang ausschließlich Protokolle sammeln, ohne Merges zu blockieren. Nachdem die Ursachen von Fehlalarmen geklärt sind, werden zunächst deterministische errors als Fehler gewertet. Anschließend lässt sich das Quality Gate für neue warnings schrittweise aktivieren. Das Skript selbst sollte im Repository versioniert und sowohl auf lokalen als auch auf Cloud-Macs verwendet werden. Dadurch bleibt keine zweite Implementierung in der CI-Konfiguration verborgen.
Die abschließende Checkliste umfasst folgende Punkte: Die Toolchain ist festgelegt; die gescannten Verzeichnisse haben eindeutige Ausschlussregeln; jeder Auftrag verwendet ein eigenes temporäres Verzeichnis; das Deployment-Target stammt aus der Projektkonfiguration; Protokolle werden bei Fehlern als Anhänge aufbewahrt; Lokalisierungsschlüssel werden nur verglichen und nicht überschrieben; der vollständige Scheme-Build wird weiterhin ausgeführt. Dadurch werden Probleme mit UI-Ressourcen bereits in einer nur wenige Dutzend Sekunden dauernden Vorabprüfung sichtbar, anstatt erst am Ende der Archivierung einen schwer reproduzierbaren Fehler zu hinterlassen.
Häufig gestellte Fragen
Ersetzt die ibtool-Prüfung einen vollständigen Xcode-Build?
Nein. Sie prüft UI-Ressourcen schnell auf Struktur- und Kompilierungsfehler, der tatsächliche Scheme muss aber weiterhin gebaut und getestet werden.
Warum schlägt ein lokal geöffnetes Storyboard in der CI fehl?
Häufig unterscheiden sich Xcode-Auswahl, Deployment-Ziel, Groß- und Kleinschreibung eines Pfads oder die Verfügbarkeit des Moduls mit eigenen Klassen.
Soll jeder Hinweis von ibtool die Pipeline stoppen?
Nein. Stoppen Sie zunächst bei errors, vergleichen Sie warnings mit einer geprüften Basis und speichern Sie notices als Diagnoseartefakte.
Die nächste Aufgabe auf einem exklusiven physischen Knoten ausführen
Wählen Sie Knoten und Abrechnungszeitraum und starten Sie die Bereitstellung mit der festen Konfiguration aus M4, 16 GB RAM und 256 GB SSD. Die tatsächliche Verfügbarkeit richtet sich nach der Echtzeitanzeige in der Konsole.