Engineering-Leitfaden

Große iOS-Repositories auf einem Cloud-Mac effizient abrufen

Große iOS-Repositories auf einem Cloud-Mac effizient abrufen

Wenn ein iOS-Repository mit einer mehrjährigen Commit-Historie, Design-Quelldateien, Bildschirmaufnahmen und Test-Fixtures erstmals auf einem Cloud-Mac bereitgestellt wird, liegt die Vermutung nahe, dass CPU oder Datenträger für die langsame Vorbereitung verantwortlich sind. Der eigentliche Engpass entsteht jedoch häufig schon vor dem Build: Git lädt sämtliche regulären Objekte herunter, Git LFS ruft beim Checkout automatisch alle großen Dateien ab, und am Ende kompiliert die CI nur eine der enthaltenen Apps. Statt das gesamte Verzeichnis undifferenziert zu cachen, sollten daher zunächst die Eingaben fixiert und anschließend Commit-Objekte, Arbeitsverzeichnispfade und LFS-Inhalte getrennt gesteuert werden.

Kosten des Checkouts zuerst messen

Es genügt nicht, nur die Laufzeit von git clone zu erfassen. Die Messung sollte mindestens in Netzwerkübertragung, Aufbau des Arbeitsverzeichnisses und LFS-Download aufgeteilt werden. Außerdem sollten die folgenden Ergebnisse gespeichert werden:

git --version
git lfs version
du -sh .git
du -sh .git/lfs 2>/dev/null || true
du -sh .
git count-objects -vH
git lfs ls-files | wc -l

Der erste Lauf und ein Lauf mit wiederverwendetem Arbeitsverzeichnis müssen getrennt gemessen werden. Der erste Lauf zeigt die Kosten der Übertragung vom Remote-Repository. Bei der Wiederverwendung werden dagegen ineffektive Bereinigungen, das Wachstum historischer Objekte und ein aufgeblähter LFS-Cache sichtbar. Neben dem Branch-Namen muss auch der endgültige Commit-Wert protokolliert werden:

git rev-parse HEAD
git status --short

Ziel der Optimierung ist nicht, das Verzeichnis lediglich kleiner erscheinen zu lassen. Derselbe Commit, dieselbe Verzeichnismenge und dieselben LFS-Objekte müssen zuverlässig reproduzierbar sein.

Partial Clone und Sparse Checkout kombinieren

Bei einem Partial Clone verschiebt blob:none den Download regulärer Dateiinhalte. Sparse Checkout begrenzt dagegen die Verzeichnisse, die tatsächlich im Arbeitsverzeichnis ausgecheckt werden. Beide Verfahren lassen sich kombinieren, beschränken Git LFS jedoch nicht automatisch. Deshalb muss der erste Smudge-Vorgang ausdrücklich übersprungen werden.

export GIT_LFS_SKIP_SMUDGE=1
git clone --filter=blob:none --no-checkout "$REPO_URL" app
cd app
git lfs install --local
git sparse-checkout init --cone
git sparse-checkout set App Packages Shared
git fetch --depth=1 origin "$BUILD_REF"
git checkout --detach FETCH_HEAD
git rev-parse HEAD

Der Modus --cone eignet sich für Projekte, die nach Verzeichnissen strukturiert sind. Seine Regeln sind einfach, und Dateien aus übergeordneten Verzeichnissen verschwinden seltener unbeabsichtigt. App, Packages und Shared müssen durch die tatsächlichen Build-Abhängigkeiten ersetzt werden. Liegen der Workspace, Skripte oder Konfigurationsdateien im Stammverzeichnis des Repositorys, muss geprüft werden, ob sie weiterhin enthalten sind.

Ein Partial Clone setzt voraus, dass das Remote-Repository die Objektfilterung unterstützt. Gibt der Befehl eine Warnung zur Filterfunktion aus, ist dies als Rückfall auf einen regulären Clone zu behandeln. In diesem Fall muss die Größe von .git erneut erfasst werden; es darf nicht davon ausgegangen werden, dass die Optimierung aktiv ist.

Nur die für den aktuellen Job erforderlichen LFS-Objekte abrufen

Nach dem Checkout können LFS-Dateien weiterhin nur als Pointer vorliegen. Zunächst werden die vom aktuellen Commit referenzierten Objekte aufgelistet. Anschließend werden nur die für den jeweiligen Job benötigten Pfade abgerufen:

git lfs ls-files
git lfs pull --include="App/Assets/**,Shared/Fixtures/**"
git lfs fsck

Pfadfilter müssen sich an den Build-Eingaben orientieren und dürfen nicht anhand vermuteter Dateiendungen festgelegt werden. UI-Tests können von Bildern, Videos und lokalisierten Fixtures abhängen. Werden nur Quellcodeverzeichnisse abgerufen, kann die Kompilierung erfolgreich sein, während die Tests erst zur Laufzeit fehlschlagen.

Prüfung Erwartetes Ergebnis Häufiges Problem
git lfs ls-files Die im aktuellen Commit per LFS verwalteten Dateien werden angezeigt .gitattributes wurde nicht mit dem Ziel-Commit ausgecheckt
Prüfung des Dateikopfs Die Datei enthält den tatsächlichen Binärinhalt Die Datei ist weiterhin ein Pointer mit oid sha256
git lfs fsck Die lokalen Objekte bestehen die Integritätsprüfung Der Download wurde unterbrochen oder das Objektverzeichnis ist beschädigt
git status --short Das Arbeitsverzeichnis enthält keine unbeabsichtigten Änderungen Ein Werkzeug hat Ressourcendateien nach dem Checkout verändert

git lfs pull sollte nicht ohne Pfadbeschränkung in ein gemeinsames Initialisierungsskript aufgenommen werden. Andernfalls lädt jeder Job auch Ressourcen herunter, die er selbst nicht benötigt. Robuster ist es, für jede Pipeline eine eigene Menge von LFS-Eingaben zu deklarieren.

Parallelität und Zugangsdaten in der CI handhaben

Jeder CI-Job sollte ein eigenes Arbeitsverzeichnis verwenden. Wenn mehrere Jobs dasselbe beschreibbare Repository nutzen, können sich Sparse-Checkout-Regeln, Index-Sperren und temporäre LFS-Dateien gegenseitig beeinflussen. Auch wenn beide Jobs denselben Code nur lesen, dürfen sie .git/info/sparse-checkout nicht gleichzeitig ändern.

Zugangsdaten sollten ausschließlich während des Abrufs bereitgestellt und auf die für das Ziel-Repository erforderlichen Berechtigungen beschränkt werden. Nach git fetch und git lfs pull müssen temporäre Umgebungsvariablen und Konfigurationen für Credential Helper entfernt werden. Remote-Adressen mit eingebetteten Tokens dürfen nicht in den Logs erscheinen. Mit den folgenden Befehlen lässt sich prüfen, ob die ausgegebenen Werte sicher sind:

git remote -v
git config --local --get-regexp 'credential|lfs' || true

Submodule müssen separat behandelt werden. Die Regeln für Partial Clone und Sparse Checkout des Haupt-Repositorys werden nicht automatisch auf Submodule übertragen. Verwendet ein Submodul ebenfalls LFS, muss in seinem Verzeichnis eine lokale LFS-Konfiguration eingerichtet und der entsprechende Abruf ausgeführt werden.

Unsichere Bereinigung vermeiden

git lfs prune eignet sich für ein exklusiv verwendetes Repository, nicht jedoch für LFS-Objektverzeichnisse, die von mehreren Jobs gemeinsam genutzt werden. In gemeinsam genutzten Umgebungen ist es sicherer, für jeden Job ein eigenes Verzeichnis anzulegen und es nach Abschluss vollständig zu löschen. Muss ein Arbeitsverzeichnis wiederverwendet werden, ist vor der Bereinigung und Integritätsprüfung sicherzustellen, dass keine parallelen Jobs aktiv sind.

Vorabprüfung statt nachträglicher Fehlersuche

Vor dem eigentlichen Aufruf von xcodebuild sollte eine schlanke Prüfstufe ausgeführt werden. Sie muss mindestens bestätigen, dass der Commit-Wert korrekt und das Arbeitsverzeichnis unverändert ist, dass die erforderlichen Projektdateien vorhanden sind und dass LFS-Pointer durch die tatsächlichen Dateien ersetzt wurden. Zusätzlich sollte sie die Datenträgerbelegung ausgeben. Eine solche Prüfung kann beispielsweise so aussehen:

test -f App/App.xcodeproj/project.pbxproj
test -s App/Assets/LaunchVideo.mov
if grep -q "oid sha256:" App/Assets/LaunchVideo.mov; then
  exit 1
fi
git diff --exit-code
git lfs fsck
du -sh . .git .git/lfs

Abschließend sollten vier Angaben aufbewahrt werden: der Commit-Hash, die Liste der Sparse-Checkout-Verzeichnisse, die LFS-include-Regeln und das Ergebnis der Prüfung. Schlägt ein Job auf dem Cloud-Mac fehl, lässt sich damit zunächst die Vollständigkeit der Eingaben kontrollieren, bevor Compiler-, Signierungs- oder Testprotokolle untersucht werden. So werden Fehler bei der Vorbereitung des Repositorys nicht fälschlich als sporadische Build-Probleme eingeordnet.

Häufig gestellte Fragen

Ersetzen Partial Clone und Sparse Checkout Git LFS?

Nein. Partial Clone begrenzt normale Git-Objekte, Sparse Checkout steuert die Pfade im Arbeitsverzeichnis und Git LFS liefert die Inhalte der verwalteten Großdateien.

Warum liegen nach dem Checkout nur Git-LFS-Zeigerdateien vor?

Meist wurde Smudge übersprungen, ohne anschließend git lfs pull auszuführen. Prüfen Sie Commit, Include-Muster, Zugangsdaten und LFS-Konfiguration vor dem Abruf.

Soll jeder CI-Job beim Aufräumen git lfs prune ausführen?

Nur wenn Repository und LFS-Objektverzeichnis ausschließlich diesem Job gehören. Bei gemeinsamem Speicher sollte stattdessen ein isoliertes Arbeitsverzeichnis vollständig entfernt werden.

Dedizierte Cloud-Macs

Build-Aufgaben auf einem dedizierten physischen Knoten ausführen

Wählen Sie MiniRents M4 oder MiniRents M4 Pro zur Tages-, Wochen-, Monats- oder Quartalsmiete und wählen Sie den Knoten passend zu Ihrem Team und dem Standort Ihres Code-Repositorys. Die Geräte sind dedizierte physische Rechner, keine virtuellen Maschinen.

Modell auswählen und bestellen