Eine iOS-Pipeline kann die gesamte Kompilierungsphase erfolgreich durchlaufen und erst bei der Installation im Simulator eine fehlerhafte Paketstruktur offenbaren oder unmittelbar nach dem Start wieder beendet werden. Vollständige UI-Tests an den Anfang zu stellen, verlangsamt nicht nur das Feedback, sondern lässt grundlegende Fehler auch wie Timeouts aussehen. Zuverlässiger ist eine Installations- und Startprüfung auf dem Cloud-Mac, die innerhalb von zwei bis drei Minuten abgeschlossen ist: Die Laufzeitumgebung wird fest vorgegeben, die gerade erzeugte .app installiert, der Zielprozess auf seine Ausführung geprüft und das Startprotokoll gesichert.
Welche Fragen die Prüfung beantworten muss
Diese Prüfung validiert keine Geschäftsfunktionen. Sie beantwortet lediglich vier Ja-oder-Nein-Fragen: Lässt sich der Simulator ordnungsgemäß starten, kann das Artefakt installiert werden, lässt sich die angegebene Bundle ID starten und bleibt der Prozess während des Beobachtungszeitraums aktiv? Schlägt einer dieser Punkte fehl, sollten keine weiteren Ressourcen für nachgelagerte UI-Tests belegt werden.
| Prüfpunkt | Erfolgskriterium | Bei einem Fehler sichern |
|---|---|---|
| Start | bootstatus wird ordnungsgemäß beendet |
Geräte-UDID, Laufzeitversion |
| Installation | simctl install gibt 0 zurück |
App-Pfad, Befehlsausgabe |
| Aufruf | simctl launch gibt eine PID zurück |
Bundle ID, Exitcode |
| Prozessstatus | Prozess ist nach dem Beobachtungszeitraum weiterhin abfragbar | Systemprotokolle, Absturzberichte |
Der Wert einer solchen Prüfung liegt nicht in einer größeren Testabdeckung, sondern darin, „Das Artefakt ist vorhanden“ und „Das Artefakt ist ausführbar“ als zwei eindeutige Ergebnisse voneinander zu trennen.
Die Simulator-UDID sollte dem Auftrag ausdrücklich übergeben werden, statt sich auf booted zu verlassen. Auf einem gemeinsam genutzten Cloud-Mac können noch Geräte aktiv sein, die von anderen Aufträgen gestartet wurden. Ein unscharfes Ziel kann daher Protokolle und Installationsergebnisse verschiedener Aufträge vermischen. Welche konkreten Konfigurationen auf NowMini-Knoten verfügbar sind, sollte in der Konsole geprüft werden. Die Pipeline selbst muss lediglich von der installierten Xcode-Version und der zugehörigen Simulatorlaufzeit abhängen.
Build-Artefakt und Zielgerät fest vorgeben
Zunächst sollte die Build-Ausgabe in das Auftragsverzeichnis geschrieben werden, damit der Pfad nicht aus DerivedData erraten werden muss. Scheme, Konfiguration, Bundle ID und UDID sollten über Umgebungsvariablen bereitgestellt und zu Beginn des Auftrags geprüft werden.
set -euo pipefail
: "${SIM_UDID:?SIM_UDID is required}"
: "${APP_BUNDLE_ID:?APP_BUNDLE_ID is required}"
WORK_DIR="${RUNNER_TEMP:-$PWD/.smoke}"
APP_PATH="$WORK_DIR/build/Sample.app"
LOG_DIR="$WORK_DIR/logs"
rm -rf "$WORK_DIR"
mkdir -p "$LOG_DIR"
xcodebuild \
-workspace Sample.xcworkspace \
-scheme Sample \
-configuration Debug \
-sdk iphonesimulator \
-destination "platform=iOS Simulator,id=$SIM_UDID" \
CONFIGURATION_BUILD_DIR="$WORK_DIR/build" \
build
Mit find sollte nicht einfach die erste .app ausgewählt werden. Ein Projekt kann gleichzeitig die Host-App, einen Test-Host und Erweiterungen erzeugen; auch deren Reihenfolge ist nicht stabil. Wenn der Produktname bekannt ist, sollte der Pfad direkt zusammengesetzt und Info.plist vor der Installation validiert werden:
test -d "$APP_PATH"
BUILT_ID=$(/usr/libexec/PlistBuddy -c \
"Print :CFBundleIdentifier" "$APP_PATH/Info.plist")
test "$BUILT_ID" = "$APP_BUNDLE_ID"
Dieser Schritt erkennt frühzeitig ein falsch ausgewähltes Scheme, fehlerhafte Ersetzungen in der Build-Konfiguration oder einen Test-Host, der versehentlich an den Installationsbefehl übergeben wurde.
Simulator starten, App installieren und Prozess prüfen
Beim Start des Geräts muss bootstatus -b verwendet werden, um zu warten, bis das System tatsächlich einsatzbereit ist. Wird nur boot ausgeführt und unmittelbar danach installiert, kann es bei hoher Auslastung zu sporadischen Fehlern kommen, weil benötigte Dienste noch nicht verfügbar sind.
xcrun simctl boot "$SIM_UDID" 2>/dev/null || true
xcrun simctl bootstatus "$SIM_UDID" -b
xcrun simctl uninstall "$SIM_UDID" "$APP_BUNDLE_ID" \
2>/dev/null || true
xcrun simctl install "$SIM_UDID" "$APP_PATH"
LAUNCH_OUTPUT=$(xcrun simctl launch \
"$SIM_UDID" "$APP_BUNDLE_ID" \
-SmokeTestMode YES)
printf '%s
' "$LAUNCH_OUTPUT"
PID=$(printf '%s
' "$LAUNCH_OUTPUT" | awk -F': ' 'NF == 2 {print $2}')
test -n "$PID"
Startargumente können dafür sorgen, dass die App die Ersteinrichtung überspringt, Animationen deaktiviert oder lokale Fixtures verwendet. Dieses Verhalten muss jedoch ausdrücklich im Anwendungscode implementiert sein. Es darf nicht vorausgesetzt werden, dass beliebige Argumente automatisch wirksam werden. Nach dem Start folgt ein kurzer Beobachtungszeitraum; anschließend wird der Prozess innerhalb des Simulators abgefragt. Wer nur den Rückgabecode von launch prüft, übersieht eine App, die innerhalb der ersten Sekunde nach dem Start beendet wird.
sleep 8
xcrun simctl spawn "$SIM_UDID" launchctl print \
"gui/$(id -u)/$APP_BUNDLE_ID" >/dev/null
Bei einigen Anwendungen stimmt die Prozessbezeichnung nicht vollständig mit der Bundle ID überein. In diesem Fall sollte stattdessen der bekannte Prozessname abgefragt oder beim Anwendungsstart eine Bereitschaftsmarkierung geschrieben werden, die ausschließlich für Tests ausgelesen wird. Ein instabiler Zustand darf nicht dadurch kaschiert werden, dass die feste Wartezeit immer weiter verlängert wird.
Verständliche Nachweise für Fehler sichern
Die Bereinigung ist wichtig, doch zuvor müssen die Nachweise gesichert werden. Der minimale Nachweissatz umfasst die Build-Ausgabe, die Gerätebeschreibung, die Installations- und Startausgabe sowie die einheitlichen Protokolle rund um den Zielprozess. Der Protokollzeitraum sollte sich auf den aktuellen Auftrag beziehen, statt den gesamten Verlauf des Hosts zu exportieren.
xcrun simctl list devices -j > "$LOG_DIR/devices.json"
xcrun simctl spawn "$SIM_UDID" log show \
--style compact \
--last 2m \
--predicate "process == 'Sample'" \
> "$LOG_DIR/sample-launch.log" 2>&1 || true
Ein häufiger Fehler besteht darin, nur die Standardfehlerausgabe zu archivieren. Wird die App unmittelbar nach dem Start beendet, kann die eigentliche Ursache im Systemprotokoll des Simulators stehen, etwa ein Fehler beim Laden einer dynamischen Bibliothek, ein falscher Ressourcenpfad oder eine unbehandelte Ausnahme. Enthalten die Protokolle Token, Request-Header oder Benutzerdaten, müssen diese vor der Archivierung anonymisiert werden. Außerdem ist der Zugriff auf die Pipeline-Artefakte zu beschränken.
Umgebungsfehler von Anwendungsfehlern unterscheiden
Ein Fehler bei bootstatus weist normalerweise auf ein Problem mit der Geräteumgebung hin. Schlägt install fehl, sollten zuerst Paketstruktur, Laufzeitkompatibilität und freier Speicherplatz geprüft werden. War launch erfolgreich, der Prozess ist jedoch verschwunden, sind vorrangig die Anwendungsprotokolle auszuwerten. Erst nach dieser Einordnung sollte über eine Wiederholung entschieden werden. Schlägt die Anwendung mehrmals auf dieselbe Weise fehl, darf sie nicht automatisch mehrfach neu ausgeführt werden, da dies die eigentliche Rückmeldung nur verzögert.
Zustand bereinigen und Prüfung in die Pipeline integrieren
Unabhängig davon, ob der Auftrag erfolgreich ist oder fehlschlägt, müssen die Anwendung beendet und der für diesen Auftrag verwendete Simulator heruntergefahren werden. Mit einem shell-trap lässt sich sicherstellen, dass die Abschlussarbeiten immer ausgeführt werden:
cleanup() {
xcrun simctl terminate "$SIM_UDID" "$APP_BUNDLE_ID" \
2>/dev/null || true
xcrun simctl uninstall "$SIM_UDID" "$APP_BUNDLE_ID" \
2>/dev/null || true
xcrun simctl shutdown "$SIM_UDID" \
2>/dev/null || true
}
trap cleanup EXIT
Wenn jeder Auftrag exakt denselben Ausgangszustand benötigt, kann nach dem Herunterfahren simctl erase ausgeführt werden. Das verlängert jedoch die Laufzeit und entfernt auch vorbereitete Daten. Der üblichere Kompromiss besteht darin, die App zu deinstallieren, das Auftragsverzeichnis zu bereinigen und jeder Pipeline ein eigenes Gerät zuzuweisen. Sind Berechtigungszustände relevant, sollten die betroffenen Dienste zusätzlich ausdrücklich zurückgesetzt werden.
Abschließend wird diese Prüfung nach der Kompilierung und vor den Unit- oder UI-Tests eingeordnet. Das Skript muss bei einem Fehler einen eindeutigen Exitcode ungleich null zurückgeben; die Protokollarchivierung ist so zu konfigurieren, dass sie immer ausgeführt wird. Dadurch erscheint eine fehlgeschlagene Installation nicht mehr als Test-Timeout, und eine unmittelbar beendete App blockiert nicht unnötig eine ganze Gruppe von Runnern. Jeder Fehler hinterlässt zugleich genügend Nachweise, damit Entwickler ihn mit derselben Laufzeitumgebung und demselben Artefakt reproduzieren können.
Häufig gestellte Fragen
Warum reicht ein erfolgreicher xcodebuild-Lauf nicht aus?
Er bestätigt nur die Erstellung des Produkts. Fehler in der Bundle-Struktur, beim Deployment-Ziel oder in Laufzeitressourcen können Installation und Start weiterhin verhindern.
Welche Nachweise sollte eine fehlgeschlagene Prüfung sichern?
Sichern Sie die xcodebuild-Ausgabe, UDID und Laufzeitversion des Simulators, den App-Pfad, die Rückgabecodes von simctl install und launch sowie die Prozessprotokolle rund um den Fehler.
Die nächste Aufgabe auf einem exklusiven physischen Knoten ausführen
Cloud-Mac mit M4, 16 GB RAM und 256 GB SSD – wählen Sie je nach Aufgabenzeitraum einen Knoten in Singapur, Tokio, Seoul oder Hongkong.