2026-04-15 Xcode DerivedData, TMPDIR & xcresult-Isolation auf gemietetem Cloud-Mac-CI
Mobile Release-Teams, die Mac mini M4-Builder in Hongkong, Tokio, Seoul, Singapur und US East mieten, geben oft „langsames Xcode“ an, während die Ursache Dateisystemkonkurrenz ist: zwei Pipelines schreiben dieselbe DerivedData-Partition, ein UI-Testjob füllt /tmp, oder ein .xcresult-Bundle wächst über 6 GB und verhungert parallele Archive. Dieses Playbook vom 2026-04-15 erklärt, wer Job-Isolation braucht, katalogisiert reale Ausfälle auf headless-SSH-Hosts, vergleicht Strategien tabellarisch, liefert ein numeriertes Acht-Schritte-Shell-Runbook und verlinkt SwiftPM-Registry & Cache sowie automatische vs. manuelle Signierung, damit Folgeschritte deterministisch bleiben.
Apple Silicon auf Bare Metal reduziert CPU-Rauschen, aber NVMe-Schreibdurchsatz bleibt endlich. Das Teilen von ~/Library/Developer/Xcode/DerivedData erzeugt stochastische SwiftEmitModule-Fehler. Binden Sie DERIVED_DATA_PATH, TMPDIR, -resultBundlePath und -clonedSourcePackagesDirPath an ein gemeinsames Job-Präfix, dann wird Kapazitätsplanung messbar statt bauchgefühlt — auch beim Vergleich regionaler Knoten.
Wer profitiert von Job-DerivedData auf Cloud-Macs
Isolation zahlt sich aus, wenn innerhalb eines rollierenden 24-Stunden-Fensters drei oder mehr gleichzeitige xcodebuild-Prozesse dieselbe Workspace-Familie berühren können — typisch für trunk-basierte iOS-Shops oder White-Label-Fabriken. Ohne eigene Pfade bleiben Modulkarten und XCTest-Anhänge fragil. Vorhersagbare Bäume pro Job machen die Frage „zweites M4?“ datengetrieben.
Fehlermuster auf headless-SSH-Buildern
- Index während Build — Hintergrundindexierung aus Job A mutiert Indizes, die Job B mittendrin liest („Typ nicht gefunden“ bis Clean).
- Gemeinsames TMPDIR — SwiftPM/clang erzeugen massenhaft kleine Dateien; ein Cleanup eines anderen Jobs löscht noch referenzierte Präfixe.
- xcresult ohne Quoten — UI-Tests mit Video + Screenshots: drei Suites > 8 GB.
- Archiv + Tests verzahnt — Nachtjobs archivieren und testen auf demselben DerivedData-Pfad; abgebrochene Archive erhöhen Korruptionsrisiko.
DERIVED_DATA_PATH, TMPDIR, Ergebnispfad und -clonedSourcePackagesDirPath als ein Job-Präfix, das bei Queue-Zeit entsteht und per trap endet.
Isolationsstrategien im Vergleich
| Ansatz | Pro | Contra | Wann |
|---|---|---|---|
Per-job DERIVED_DATA_PATH unter /Volumes/builds |
Deterministische Cleans, Quoten pro Pipeline | Erster Build ohne Warmcache langsamer | Parallele CI auf geteiltem Mac mini M4 |
| Read-only Warmcache + Overlay-Kopie | Schnellere Kaltkompilate | Komplexes rsync, schwierig rein SSH | Release-Züge mit identischen Xcode-Minoren |
| Separate macOS-Benutzerkonten | Harte Isolation von Signing-Identitäten | Höhere Ops-Kosten und Lizenzen | Regulierte Mandanten mit gemischten Apps |
NVMe-Budget: Zahlen fürs Runbook
| Artefakt | Typischer Dauerzustand | Spitze UI-Tests |
|---|---|---|
| DerivedData (mittlere App, Debug) | 6–14 GB | + 3 GB Scratch |
| SwiftPM Checkout + Build | 1–4 GB | + 2 GB bei neuen Tags |
| xcresult-Bundle | 400–900 MB Unit-Tests | 3–10 GB mit Video |
Acht-Schritte-Runbook für gemietete Cloud-Macs
- Job stempeln —
JOB_ID=$(date +%s)-$RANDOM,ROOT=/Volumes/builds/$JOB_ID, Unterordner anlegen. - Umgebung pinnen —
DERIVED_DATA_PATH,TMPDIR,TEMP,TMPsetzen. - trap — nach Kollisionscheck
trap 'rm -rf "$ROOT"' EXIT; Langläufer zuerst Artefakte archivieren. - xcodebuild — Tests mit
-resultBundlePath "$ROOT/results/Test-$JOB_ID.xcresult"; IPA laut IPA-Export in Objektspeicher. - SwiftPM —
-clonedSourcePackagesDirPath "$ROOT/spm"plus Registry-Auth wie im SwiftPM-Artikel. - Parallelitätsdeckel — bei 24 GB RAM schwere UI-Tests auf ≤2; Mutex in der Queue.
- Upload —
ditto -c -k --sequesterRsrc --keepParent, Checksumme, dann lokales zip löschen. - Telemetrie —
df -hvor/nach; Alarm bei Wandzeit > 42 Min und > 90 % Platte (Anhänge).
export JOB_ROOT=/Volumes/builds/ci-$CI_PIPELINE_ID
mkdir -p "$JOB_ROOT"/{dd,tmp,res,spm}
export DERIVED_DATA_PATH="$JOB_ROOT/dd" TMPDIR="$JOB_ROOT/tmp" TEMP="$JOB_ROOT/tmp" TMP="$JOB_ROOT/tmp"
Metrik-Hooks für einen Nachmittag
Ohne Messung ist Verzeichnis-Isolation wirkungslos. Drei Gauges: (1) du -sk "$ROOT" nach Compile, (2) Wandzeit xcodebuild archive, (3) freie GB auf dem Volume mit /Volumes/builds. Wenn die mediane DerivedData-Größe wöchentlich > 18 % steigt, prüfen Sie inkrementelles Deaktivieren oder Codegen — nicht sofort Hardware. Loggen Sie xcodebuild -version neben JOB_ID, um Wechsel zwischen 16.2 und 16.3 mit Spike zu korrelieren. xcrun simctl delete unavailable außerhalb von Peak-Fenstern planen.
Brücke zu SwiftPM & Signing-Disziplin
DerivedData-Isolation ersetzt keine Provisioning-Hygiene. Wöchentliche Zertifikatsrotation braucht Auto- vs. Manual-Signing, damit codesign nicht auf UI wartet. Halten Sie Package.resolved versioniert und mischen Sie kein globales SourcePackages mit per-job DerivedData — Checkout-Sperren bleiben Wettläufe.
FAQ: DerivedData auf gemietetem Apple Silicon
| Frage | Praktische Antwort |
|---|---|
| DerivedData per Symlink auf NFS? | Vermeiden, außer RTT < 2 ms; lieber lokales NVMe + Artefakt-Upload. |
| Xcode Server Legacy? | Moderne CI ignoriert /Library/Developer/XcodeServer; immer explizite Pfade setzen. |
| Rosetta-Mix? | x86_64-Simulatoren nicht mit arm64-Präfixen mischen; architekturgetrennt isolieren. |
Warum regionale Mac-mini-M4-Knoten weiter zählen
Verzeichnis-Isolation beseitigt Software-Races, nicht die Physik: Ingenieure in Tokio, die Artefakte aus US East ziehen, zahlen RTT in Minuten. Colokalisieren Sie Builder nahe SCM und Tester; das MacXCode-Fußabdruck HK / JP / KR / SG / US hält /Volumes/builds auf dem Kontinent Ihrer Git-LFS- und Audit-Logs. Lesen Sie Hilfe zu SSH-Baselines, skalieren Sie erst nach 14 grünen Nächten Disk-Telemetrie.
Fazit: Per-job DERIVED_DATA_PATH + TMPDIR + explizite xcresult-Pfade machen „zufällige rote Builds“ zu messbaren IO-Budgets — dann tragen Preise statt Bauchgefühl.
Isolierte Builder auf Bare-Metal-M4 skalieren
HK · JP · KR · SG · US · SSH / VNC