2026-04-25 xcodebuild test Code Coverage, xcresult zu JUnit & PR-Gates auf gemietetem Cloud-Mac (HK / JP / KR / SG / US)
iOS- und macOS-Teams, die headless xcodebuild test auf einem gemieteten Mac mini M4 mit Apple Silicon fahren, mergen Pull Requests oft noch allein wegen Exit-Code und grün/rot in der CI-Oberfläche. Dieser Leitfaden 2026-04-25 schließt die Lücke zwischen „Tests grün“ und „wir erklären, was sich in der Qualität diese Woche geändert hat“: -resultBundlePath in ein pro-Job-Verzeichnis unter Ihrem isolierten Build-Root, -enableCodeCoverage YES für LLVM-profdata, Export nach JUnit-XML (xcresulttool oder vertrauenswürdiger Vendor-Wrapper) und eine Linienraten-Policy mit xccov oder Dashboard-Parser. Er ergänzt—ersetzt nicht—den Test-Plan + paralleles xcresult-Runbook. Verlinken Sie Swift 6 Strict Concurrency für die Compiler-Gate-Story; dieser Artikel ist die Seite Test-Artefakte + Merge-Signal.
Was ein PR-Gate außer Exit-Code 0 sehen soll
Ein solides Gate liefert drei Pakete: (1) JUnit- (oder xUnit-) XML, mit dem die PR-UI pro Test und Flake annotieren kann; (2) ein gemergtes oder einzelnes .xcresult als komprimiertes Artefakt mit derselben Retention wie Ihre xcresult-Isolation; (3) eine Abdeckungs-Kopfzeile (Linie oder Branch) zum Diff zum Merge-Base, damit Refactorings, die toten Code löschen, nicht als Regression zählen. In HK / JP / KR / SG / US muss das Gate auf aggregierten Metriken basieren, wenn Sie nach Destination sharden—niemals nur das profdata-Fragment eines Shards, außer die Policy begrenzt die Abdeckung explizit. Drucken Sie Timings mit demselben Pool-Label wie in Metadaten, damit Produkt keinen kalten Simulator in Tokio mit einem warmen in US East vergleicht.
Wichtige xcodebuild-Flags: Result Bundle, Abdeckung, Ziele
Pinnen Sie DEVELOPER_DIR=/Applications/Xcode.app und eine Simulator--destination-Zeichenkette, kein schwebendes „latest iOS“, das Sie nicht benannt haben. Minimales Muster:
DEVELOPER_DIR=/Applications/Xcode.app xcodebuild test -workspace App.xcworkspace -scheme App -destination 'platform=iOS Simulator,name=iPhone 16,OS=18.2' -enableCodeCoverage YES -resultBundlePath "$CI_ROOT/Test-$(date +%s).xcresult" -derivedDataPath "$CI_DERIVED/job-$CI_JOB_ID" -parallel-testing-enabled YES CODE_SIGNING_ALLOWED=YES
-only-testing / -skip-testing in Shard-Jobs; ein Koordinator, der mergen kann. -retry-tests-on-failure nur mit festgeschriebenem Flake-Budget—endlose Retries verdecken defekte Keychain, wie im Signing-Artikel. Läuft ein separates Swift-6-Strict-Stadium, doppelzählen Sie nicht dieselben Unit-Tests, außer der Scheme-Split ist echt—doppelte Abdeckung kostet Minuten auf geteilter NVMe.
Von .xcresult zu JUnit: Export, der headless hält
CI-Macs stellen xcresulttool bereit: Tests und Diagnostik als JSON oder JUnit, je nach Ihrer gepinnten Xcode-Version. Typischer Ablauf: (1) -resultBundlePath muss pro Lauf eindeutig sein und darf kein Symlink auf gemeinsame NFS sein; (2) xcresulttool get --format json post-run; (3) Umwandlung in JUnit—oft ein schlanker Ruby-/Node-Adapter; entscheidend sind stabile testsuite-Namen und classname pro testcase für de-duplizierte Retries. Beim Mergen mehrerer Bundles Xcode-Merge bevorzugen oder Reihenfolge (Timestamp aufsteigend) dokumentieren und ablehnen, wenn testStatus kollidiert. Das rohe xcresult in Cold Storage legen wie bei Simulator-Triage, nicht nur XML.
LLVM profdata, xccov und verteidigbare Schwellen
Mit -enableCodeCoverage YES erzeugen Compiler/Linker Daten, die Sie mit xcrun llvm-profdata merge zusammenführen und mit xcrun xccov in passenden view --report- oder report-Modi prüfen. Pragmatische Policy: Floor für App- und Kern-Framework-Module, die Ihnen gehören, Generat- und Drittanbieterziele per Exclusion-List ausschließen—nicht aufgelisteter fremder Code darf Ihr Team-Budget nicht still verschlingen. Zahlen nach Umzug auf self-hosted runner abgleichen: derselbe git-SHA auf zwei Hosts sollte denselben Hash der Quellen liefern, aber inkrementelle Abdeckung kann abweichen, wenn ein Ziel fehlt—deshalb deaktiviert die „strict“-Spur ggf. inkrementell. Wenn Sie dSYMs hochladen, prüfen Sie, ob die dSYM-Pipeline noch zu Ihren bitcode/LLVM-Artefakten passt.
xccov profdata nicht öffnen kann—lautloses 0 % und grün führt 2026 zu ungetesteten Diffs, die später im Dashboard landen.
Geshardete test-Jobs: mergen, bevor das Gate fällt
Bei Split nach -only-testing:Target/Class oder Destination liefert jeder Shard eigenes profdata und xcresult. Ein Merge-Gate im Koordinator muss: (1) prüfen, dass jeder Shard beendet ist und Artefakte hochgeladen hat; (2) profdata mergen, bevor xccov einmal läuft; (3) JUnit aggregieren und bewusst übersprungene Suites markieren, die auf einem Shard fehlen. Fällt ein Shard Infra-Präemption zum Opfer, gilt der Merge als fehlgeschlagen, sofern keine schriftlich definierte Ausnahme existiert. Passt zu M4-Fan-Out: Mehr Parallelität nützt nur, wenn Ihre Datenfläche (Artefakte + Abdeckung) serialisierbar in einen Report fließt.
Symptom / Schicht / Maßnahme
| Symptom | Schicht | Fix / prüfen |
|---|---|---|
| 0 % Abdeckung, Tests liefen | Build-Settings / Target-Mitgliedschaft | Code Coverage im Scheme, Targets mit Abdeckungsflags bauen |
| JUnit leer, UI zeigt bestanden | Tooling-Export | xcresulttool an Host-Version; Vendor-Stubs misstrauen |
| Zwei Hosts: riesiger Linien-Delta | Branch / inkrementell / Shard | Inkrementell im Gate deaktivieren; profdata mergen; Ziele pinnen |
Weitere Runbooks und Automation auf demselben Host
Läuft hier auch ein OpenClaw-Gateway, teilen Sie kein einzelnes /tmp über Agents—TMPDIR war ohnehin pro Job. Für Release-Builds Remote-Archiv und IPA-Export via App Store Connect API bauen auf derselben Identität des Vertrauens: tests+abdeckung in der CI sollen identisch mit dem Branch-Stand sein, den Sie releasen—Promotionsverzögerungen nur dokumentieren.
FAQ: Qualitätstore in multi-region Mac-Pools
| Frage | Pragmatische Antwort |
|---|---|
| Auf Branch-Abdeckung blockieren? | Nur, wenn eure Sprachmischung das hergibt; Linienrate verkauft sich leichter an PMs, Branch für Sicherheitsmodule. |
| Wie lange xcresult-Zips aufbewahren? | An eure Incident-SLA koppeln—14+ Tage starten viele; 1–2 TB Miete erlaubt länger. |
| US East zuerst oder Asien in der Matrix? | Region wählen, in der die meisten Committer fürs interaktive Repro liegen; voll Matrix auf Release. |
Warum Mac mini M4 mieten für diese Last
Simulator- und Abdeckungsjobs sind oft stärker an CPU+NVMe+inode als an GPU gebunden. Bare-Metal Mac mini M4 in den Regionen MacXCode bieten planbares I/O und RAM für mehrere CoreSimulator-Roots warm, ohne lauten Mitmieter neben Ihrem OpenClaw-Gateway. Braucht die Region eine zweite Bahn fürs p95, skalieren Sie via Preisseite statt drei Teams auf ein 512 GB-Volume zu pressen. Gelegentlich GUI—VNC ist Notnagel; SSH + Artefakte sollen 2026 Wochenrhythmus tragen.
Reproduzierbares iOS-Test-CI in der Region
M4 · standardmäßig SSH · 1–2 TB-Optionen