ENGINEERING-NOTIZ

Gehostete Xcode-Builds oder selbst verwalteter Cloud Mac

Gehostete Xcode-Builds oder selbst verwalteter Cloud Mac

Das Team verfügt bereits über eine funktionierende iOS-Pipeline. Doch nach jeder neuen Abhängigkeit, jedem Xcode-Wechsel und jeder Anpassung der Signierung stellt sich dieselbe Frage erneut: Sollen die Builds weiterhin auf einer gehosteten Plattform laufen oder auf einen selbst verwalteten Cloud Mac verlagert werden? Vergleichen Sie nicht zuerst, wie viele Sekunden ein einzelner Build schneller ist. Für die langfristigen Kosten ist entscheidend, ob sich die Umgebung reproduzieren lässt, ob der Cache kontrollierbar ist, ob der Fehlerzustand erhalten bleibt und wie viel Betriebsaufwand die Entwickler übernehmen müssen.

Arbeitslasten zunächst in drei Kategorien einteilen

Erfassen Sie zuerst die Jobs der vergangenen zwei Wochen, statt die Plattform nach Bauchgefühl auszuwählen. Empfehlenswert ist eine Unterteilung in Merge-Prüfungen, Release-Archive und Ad-hoc-Diagnosen. Merge-Prüfungen sind meist kurzlebig, haben klar definierte Eingaben und können nach einem Fehler erneut ausgeführt werden. Release-Archive erfordern Signierungsmaterial, die Aufbewahrung von Artefakten und eine strikt festgelegte Versionierung. Bei Ad-hoc-Diagnosen müssen Entwickler dagegen auf dieselbe Umgebung zugreifen können, um Probleme zu reproduzieren.

Mindestens folgende Daten sollten erfasst werden:

Bereich Zu erfassende Angaben Zweck der Bewertung
Toolchain Versionen von macOS, Xcode, Ruby und Paketmanagern Aufwand für die Fixierung der Umgebung einschätzen
Eingaben Commit-ID, Lockfiles und Build-Parameter Reproduzierbarkeit des Jobs beurteilen
Phasen Dauer von Warteschlange, Ausführung, Upload und Bereinigung Tatsächliche Engpässe erkennen
Fehlerzustand Protokolle, Result Bundle und Archivartefakte Möglichkeiten der Fehleranalyse bewerten
Cache Pfade, Größe, Trefferbedingungen und Invalidierungsverfahren Langfristigen Nutzen einschätzen

Ein einzelner erfolgreicher Lauf belegt noch keinen stabilen Prozess. Führen Sie denselben Commit mindestens dreimal hintereinander aus. Testen Sie das Cache-Verhalten anschließend erneut mit einem Commit, der nur den Anwendungscode, nicht aber die Abhängigkeiten ändert.

Einheitlichen Fingerabdruck der Build-Umgebung erstellen

Beide Ansätze müssen dasselbe Skript zur Prüfung der Umgebung verwenden. Geben Sie nicht nur xcodebuild -version aus, sondern erfassen Sie auch die Systemversion, das aktuell ausgewählte Entwicklerverzeichnis, das SDK sowie den Zustand von Ruby und den Paketmanagern. Die Fingerabdruckdatei sollte bei jedem Job als reguläres Artefakt gespeichert werden. Vergleichen Sie bei einem Fehler zuerst diese Datei, bevor Sie den Anwendungscode untersuchen.

#!/bin/zsh
set -euo pipefail

mkdir -p artifacts
{
  sw_vers
  uname -m
  xcodebuild -version
  xcode-select -p
  xcrun --sdk iphoneos --show-sdk-version
  ruby --version
  git --version
} > artifacts/environment.txt

export DEVELOPER_DIR="/Applications/Xcode.app/Contents/Developer"
xcodebuild \
  -workspace App.xcworkspace \
  -scheme App \
  -configuration Release \
  -derivedDataPath "$PWD/.build/DerivedData" \
  -resultBundlePath "$PWD/artifacts/App.xcresult" \
  build

DEVELOPER_DIR darf nicht auf einen ungeprüften Pfad verweisen. In einer selbst verwalteten Umgebung können mehrere Xcode-Versionen installiert bleiben, die Pipeline muss jedoch ausdrücklich eine davon auswählen. In einer gehosteten Umgebung sollte die tatsächliche Version zu Beginn des Jobs geprüft werden. Bei einer Abweichung muss der Job möglichst früh beendet werden. So treten Unterschiede beim SDK nicht erst während der Archivierung zutage.

Cache, Signierung und Parallelität getrennt prüfen

Nicht das gesamte Verzeichnis als Cache übertragen

Ein Cache-Schlüssel sollte mindestens die Xcode-Hauptversion, die Architektur und den Hash der Lockfiles für Abhängigkeiten enthalten. SwiftPM, CocoaPods und temporäre Build-Verzeichnisse sollten getrennt verwaltet werden, da sie unterschiedlichen Invalidierungsbedingungen unterliegen. Packen Sie nicht das gesamte Benutzerverzeichnis und stellen Sie es nicht vollständig wieder her. Andernfalls gelangen alte Berechtigungen, temporäre Dateien und nicht sichtbare Konfigurationen in neue Jobs.

Ein selbst verwalteter Cloud Mac eignet sich für Pipelines, die große Abhängigkeits-Caches dauerhaft vorhalten müssen. Dafür benötigt jedes Repository ein eigenes Arbeitsverzeichnis. Außerdem muss regelmäßig geprüft werden, ob ein vollständiger Build auch nach dem Löschen des Caches gelingt. Bei gehosteten Builds wird die Lebensdauer des Caches üblicherweise von der Plattform gesteuert. Aus technischer Sicht sollte der Cache daher als Optimierung und nicht als Voraussetzung für einen erfolgreichen Build behandelt werden.

Signierungsmaterial nur während des Jobs verfügbar machen

Signierungsmaterial sollte zu Beginn des Jobs aus einem kontrollierten Speicher eingebunden und jede temporäre Kopie unmittelbar danach entfernt werden. Passwörter, Schlüsselinhalte oder vollständige Dateipfade dürfen nicht in Protokollen erscheinen. Unabhängig vom gewählten Ansatz muss geprüft werden, ob die Bereinigung auch bei fehlgeschlagenen Jobs ausgeführt wird und nicht nur bei erfolgreichen.

Parallelität sollte zunächst mit zwei Jobs getestet werden. Dabei ist zu beobachten, ob sie DerivedData, Simulatoren, die Keychain oder feste Ports gemeinsam verwenden. Sobald ein gemeinsam genutzter, beschreibbarer Zustand vorhanden ist, kann eine höhere Parallelität sporadische Fehler verstärken.

Wiederherstellbarkeit mit Fehlerübungen testen

Warten Sie nicht auf einen echten fehlgeschlagenen Release, bevor Sie den Wiederherstellungsablauf prüfen. Dafür eignen sich vier zerstörungsfreie Übungen: Geben Sie absichtlich ein nicht vorhandenes Scheme an, löschen Sie einen Abhängigkeits-Cache, lassen Sie einen Test fehlschlagen und beenden Sie einen Job vor der Archivierung. Kontrollieren Sie bei jedem Durchlauf den Exit-Code, das Ende des Protokolls, das Result Bundle, temporäres Signierungsmaterial und den Zustand des Arbeitsverzeichnisses.

Bei gehosteten Builds ist vor allem zu prüfen, ob die Protokolle den Fehler ausreichend erklären, ob die Umgebung reproduziert werden kann und ob bei einer erneuten Ausführung exakt dieselben Eingaben verwendet werden. Bei einem selbst verwalteten Cloud Mac muss zusätzlich sichergestellt sein, dass verbliebene Prozesse, eingebundene Verzeichnisse und belegte Ports den nächsten Job nicht beeinträchtigen. Empfehlenswert sind ein eindeutiges Verzeichnis pro Job und ein Exit-Hook, der untergeordnete Prozesse beendet.

workdir="$(mktemp -d "$PWD/.job.XXXXXX")"
cleanup() {
  jobs -p | xargs -r kill 2>/dev/null || true
  rm -rf "$workdir"
}
trap cleanup EXIT INT TERM

Das Bereinigungsskript muss zunächst in einem Testverzeichnis geprüft werden, damit zu weit gefasste Platzhalter keine anderen Jobs beeinträchtigen. Aufzubewahrende Protokolle und Build-Artefakte sollten vor der Bereinigung in ein separates artifacts-Verzeichnis kopiert werden.

Nach Kontrolle statt nach Schlagworten entscheiden

Wenn ein Projekt übliche Abhängigkeiten verwendet, die Jobs kurz sind, fehlgeschlagene Läufe einfach wiederholt werden können und das Team keinen Maschinenzustand verwalten möchte, sind gehostete Builds in der Regel besser geeignet. Müssen mehrere Xcode-Versionen fest vorgegeben, Caches langfristig wiederverwendet, spezielle Werkzeuge ausgeführt, Fehler direkt in der betroffenen Umgebung untersucht oder lang laufende Jobs dauerhaft betrieben werden, lassen sich mit einem selbst verwalteten Cloud Mac klarere Grenzen schaffen.

Beide Ansätze können auch kombiniert werden: Merge Requests führen schlanke Tests aus, während stabile Branches in der selbst verwalteten Umgebung archiviert und eingehend diagnostiziert werden. Entscheidend ist, dass beide Seiten denselben Umgebungsfingerabdruck, dieselben Lockfiles, denselben Build-Einstiegspunkt und dieselben Regeln zur Benennung von Artefakten verwenden. Andernfalls entstehen zwei inkompatible Pipelines.

Für die abschließende Bewertung zählt nicht der schnellste Einzellauf. Maßgeblich sind die Erfolgsquote über mehrere Durchläufe, die Anzahl der Umgebungsabweichungen, die Wiederherstellbarkeit nach dem Löschen des Caches, die Vollständigkeit der Fehlerbelege und die erforderlichen manuellen Eingriffe. Halten Sie diese Ergebnisse in einer Entscheidungsdokumentation im Repository fest. Vereinbaren Sie außerdem eine erneute Bewertung nach Änderungen an der Xcode-Hauptversion, am Abhängigkeitssystem oder am Umfang der Jobs, damit die gewählte Lösung nicht zu einer dauerhaften, ungeprüften Annahme wird.

Häufig gestellte Fragen

Sind gehostete Builds immer einfacher zu betreiben?

Nein. Sie passen zu standardisierten Projekten, während feste Xcode-Versionen, dauerhafte Caches, besondere Werkzeuge und detaillierte Fehleranalysen oft selbst verwaltet einfacher sind.

Kann ein Team beide Build-Modelle kombinieren?

Ja. Merge-Prüfungen können gehostet laufen, während Archive mit stabilen Caches, komplexer Signierung oder vollständigen Diagnoseprotokollen den Cloud Mac nutzen.

Was sollte vor der Auswahl geprüft werden?

Führen Sie denselben Commit mit identischen Lockdateien und Befehlen mehrfach aus und vergleichen Sie Erfolgsquote, Umgebungsdrift, Cachetreffer, Protokolle und Wiederherstellungszeit.

VMDebug physische Macs in der Cloud

Sie benötigen einen physischen Mac mini zur exklusiven Nutzung in einem Einzelauftrag?

Prüfen Sie die Konfigurationen M4 und M4 Pro, vier Mietlaufzeiten und fünf verfügbare Knoten und wählen Sie anschließend das passende Gerät für Ihren Workflow.

Konfiguration auswählen und bestellen