DevOps / CI·CD 25. April 2026

2026-04-25 xcodebuild test Code Coverage, xcresult zu JUnit & PR-Gates auf gemietetem Cloud-Mac (HK / JP / KR / SG / US)

MacXCode Engineering Team 25. April 2026 ~18 Min. Lesezeit

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.

CI-Sicherheitsbügel: Build fehlschlagen lassen, wenn 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

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 GUIVNC ist Notnagel; SSH + Artefakte sollen 2026 Wochenrhythmus tragen.

Reproduzierbares iOS-Test-CI in der Region

M4 · standardmäßig SSH · 1–2 TB-Optionen