Wenn auf demselben Cloud-Mac fortlaufend React-Native-Builds ausgeführt werden, sind häufig nicht Compilerfehler am schwierigsten zu diagnostizieren, sondern Fälle, in denen der Code geändert wurde, das Build-Artefakt jedoch unverändert wirkt. Ein lokaler Neustart behebt das Problem, während CI-Jobs gelegentlich weiterhin alte Module verwenden. Nach dem Löschen des gesamten Repositorys verschwindet der Fehler vorübergehend, tritt einige Tage später aber erneut auf. Die Ursache liegt meist darin, dass vier Zustandsebenen nicht sauber voneinander getrennt sind: Git-Arbeitsverzeichnis, Watchman-Überwachung, Metro-Transformationscache und Xcode DerivedData.
Zuerst die betroffene Cache-Ebene bestimmen
Löschen Sie bei Auffälligkeiten nicht sofort sämtliche Caches. Eine vollständige Bereinigung verschleiert die Fehlerquelle und verlangsamt nachfolgende Builds. Grenzen Sie den Fehler zunächst mit einer minimalen Änderung ein, etwa an einer Zeichenfolge, die nur in das Development-Bundle einfließt. Prüfen Sie anschließend separat den Dateiinhalt, den Metro-Modulgraphen und das endgültige App-Artefakt.
Nachweise für jeden Job erfassen
Pro Job sollten mindestens der Commit-Hash, der absolute Pfad des Arbeitsverzeichnisses, die Node- und Xcode-Versionen, der zum Starten von Metro verwendete Befehl sowie der DerivedData-Pfad protokolliert werden. Speichern Sie außerdem die Ausgabe der folgenden Befehle:
set -euo pipefail
printf 'commit=%s\n' "$(git rev-parse HEAD)"
printf 'workspace=%s\n' "$PWD"
node --version
xcodebuild -version
watchman watch-list || true
find "$PWD" -maxdepth 2 -name metro.config.js -print
Sind Commit und Quelldateien korrekt, das JavaScript-Bundle aber veraltet, sollte zuerst Metro geprüft werden. Fehlen aktualisierte native Codebestandteile im Archiv, prüfen Sie DerivedData, die Build-Konfiguration und das tatsächlich geöffnete Workspace. Meldet Watchman Fehler oder überwacht ein übergeordnetes Verzeichnis, korrigieren Sie zunächst die Überwachungsgrenzen.
Eine Bereinigung ist eine Reparaturmaßnahme, keine Diagnose. Ermitteln Sie zuerst die fehlerhafte Ebene und löschen Sie anschließend nur deren Zustand.
Jedem Job ein eigenes Arbeitsverzeichnis zuweisen
Parallele Jobs dürfen nicht im selben Checkout-Verzeichnis zwischen Branches wechseln. Ebenso sollten mehrere Jobs nicht unter demselben von Watchman überwachten Elternverzeichnis liegen. Empfehlenswert ist ein Pfad, der die Pipeline- und Jobnummer enthält, beispielsweise /Users/ci/work/4821/ios-release. Nach Abschluss eines Jobs lässt sich das gesamte Verzeichnis unabhängig archivieren oder löschen.
Das Arbeitsverzeichnis muss ein reales Verzeichnis sein und darf kein symbolischer Link sein, dessen Ziel fortlaufend geändert wird. Einige Skripte lassen einen festen Pfad auf den „aktuellen Build“ verweisen. Watchman und die Toolchain können jedoch den zuvor aufgelösten Pfad beibehalten. Der Befehl scheint dann im neuen Verzeichnis zu laufen, während die Überwachung weiterhin auf den alten Checkout zeigt.
JOB_ROOT="/Users/ci/work/${PIPELINE_ID}/${JOB_ID}"
WORKSPACE="${JOB_ROOT}/repo"
DERIVED_DATA="${JOB_ROOT}/DerivedData"
METRO_CACHE="${JOB_ROOT}/metro-cache"
mkdir -p "$WORKSPACE" "$DERIVED_DATA" "$METRO_CACHE"
export TMPDIR="${JOB_ROOT}/tmp/"
mkdir -p "$TMPDIR"
Verwenden Sie für Verzeichnisnamen ausschließlich stabile ASCII-Zeichen und vermeiden Sie Leerzeichen sowie dynamische symbolische Links. Ein eigenes Stammverzeichnis pro Job erleichtert außerdem die Überprüfung des Speicherverbrauchs und der Zuständigkeit für die Bereinigung.
Den Überwachungsbereich von Watchman begrenzen
Führen Sie nach dem Wechsel in das Arbeitsverzeichnis watchman watch-project "$WORKSPACE" aus und prüfen Sie, ob der zurückgegebene Wert für watch tatsächlich dem erwarteten Projektstamm entspricht. Wird die Überwachung auf ein gemeinsam genutztes Elternverzeichnis hochgestuft, sind meist die Repository-Grenzen unklar oder eine Konfiguration im Elternverzeichnis beeinflusst die Erkennung.
Legen Sie im Repository-Stamm eine .watchmanconfig an und schließen Sie große Verzeichnisse aus, die nicht zum JavaScript-Abhängigkeitsgraphen gehören:
{
"ignore_dirs": [
".git",
"DerivedData",
"build",
"artifacts"
]
}
Nach einer Änderung dieser Datei sollte die Überwachung für das aktuelle Projekt neu eingerichtet werden. Führen Sie auf Rechnern mit parallelen Jobs nicht routinemäßig watchman watch-del-all aus, da dadurch auch die Überwachungen anderer Jobs entfernt werden. Sicherer ist es, jedes Arbeitsverzeichnis als eigenständigen Überwachungsstamm einzurichten und beim Jobende nur Folgendes auszuführen:
watchman watch-del "$WORKSPACE" || true
Meldet dieser Befehl, dass das Ziel kein Überwachungsstamm ist, prüfen Sie zuerst watchman watch-list, statt Pfade zu erraten und Überwachungen gesammelt zu löschen.
Metro- und Xcode-Caches explizit verwalten
Der Metro-Cache sollte an den jeweiligen Job oder die Codebasis gebunden sein und sich nicht auf historische Dateien unbekannter Herkunft im temporären Systemverzeichnis stützen. Das Startskript muss den Cache-Speicherort als expliziten Parameter übergeben. Da sich der genaue Parametername je nach Metro-Version ändern kann, ist die Hilfeausgabe der im Projekt festgeschriebenen Version maßgeblich. Unabhängig davon, ob ein Befehlsparameter oder die Projektkonfiguration verwendet wird, muss der Cache letztlich unter ${METRO_CACHE} liegen, statt dass mehrere Jobs dasselbe Standardverzeichnis gemeinsam nutzen.
--reset-cache sollte nur einmalig zur Wiederherstellung eingesetzt werden, nachdem eine Beschädigung des Transformationscaches bestätigt wurde. Muss jeder Job den Cache zurücksetzen, ist die Isolation noch nicht vollständig umgesetzt. Legen Sie auch für Xcode DerivedData fest im Jobverzeichnis ab:
xcodebuild \
-workspace App.xcworkspace \
-scheme App \
-configuration Release \
-derivedDataPath "$DERIVED_DATA" \
build
Pods, Swift-Pakete und JavaScript-Abhängigkeiten dürfen verifizierte Download-Caches verwenden. „Download-Cache“ und „Build-Zustand“ müssen jedoch getrennt bleiben. Ersterer kann anhand von Lockfile-Schlüsseln wiederverwendet werden, letzterer darf ausschließlich einem einzelnen Job gehören. Prüfen Sie nach der Wiederherstellung eines Caches mindestens den Hash des Lockfiles; ein Treffer allein anhand des Branch-Namens reicht nicht aus.
Ebenenweise bereinigen statt alles zurückzusetzen
Beginnen Sie bei Auffälligkeiten mit der Ebene, deren Bereinigung die geringsten Auswirkungen hat. Beenden Sie zunächst den aktuellen Metro-Prozess, löschen Sie den Metro-Cache dieses Jobs und starten Sie Metro neu. Besteht das Problem weiterhin, entfernen Sie die Watchman-Überwachung des aktuellen Arbeitsverzeichnisses und registrieren Sie sie neu. Löschen Sie DerivedData für diesen Job nur dann, wenn die Ergebnisse der nativen Kompilierung voneinander abweichen. Ein erneuter Checkout des Repositorys sollte erst als letzte Maßnahme erwogen werden.
Vergewissern Sie sich vor der Bereinigung, dass keine Prozesse mehr auf die Verzeichnisse zugreifen:
pgrep -af "$WORKSPACE" || true
lsof +D "$JOB_ROOT" 2>/dev/null | head -n 50 || true
Das Exit-Skript des Jobs sollte außerdem prüfen, ob der Pfad des Arbeitsverzeichnisses unter dem erwarteten Stammverzeichnis liegt. So verhindern Sie, dass bei einer leeren Variablen versehentlich ein Benutzerverzeichnis gelöscht wird. Mit case lässt sich eine Positivliste zulässiger Pfadpräfixe prüfen, bevor rm -rf -- "$JOB_ROOT" ausgeführt wird.
Führen Sie zur Abnahme denselben Commit zweimal hintereinander aus. Beim zweiten Durchlauf darf der Download-Cache wiederverwendet werden, Arbeitsverzeichnis, Metro-Zustand und DerivedData müssen jedoch weiterhin auf Jobebene isoliert bleiben. Unterscheiden sich die beiden Artefakte, vergleichen Sie zunächst generierte Dateien, Umgebungsvariablen und Build-Protokolle, statt den Umfang der Bereinigung sofort auszuweiten. Ein so aufgebauter Ablauf behebt nicht nur ein einzelnes Cache-Problem, sondern hinterlässt auch genügend Nachweise, um die nächste Abweichung erklären zu können.
Häufig gestellte Fragen
Sollte jeder Build Metro mit reset-cache starten?
Nein. Ein eigener Cache-Pfad pro Auftrag reicht im Normalbetrieb aus. Ein Reset ist nur bei einem nachgewiesen fehlerhaften Modulgraphen oder Transformationscache sinnvoll.
Dürfen parallele Aufträge dieselbe Watchman-Wurzel verwenden?
Besser nicht. Verwenden Sie getrennte, nicht verschachtelte Arbeitsverzeichnisse und entfernen Sie nach dem Auftrag nur die zugehörige Überwachung.
Wählen Sie einen dauerhaft verfügbaren Cloud-Mac für Ihren nächsten Xcode-Build
Prüfen Sie Chip, Arbeitsspeicher, Speicher, Abrechnungszeitraum und Knotenregion, bevor Sie mit der Konfiguration beginnen. Der tatsächlich verfügbare Status wird in Echtzeit von der Konsole angezeigt.