Ein iOS-Projekt lässt sich auf einem Entwicklungsrechner möglicherweise problemlos klonen, bleibt in einer Cloud-Mac-CI-Umgebung jedoch bei den Submodulen hängen: Das Haupt-Repository wurde ausgecheckt, private Komponenten melden Berechtigungsfehler, verschachtelte Abhängigkeiten verbleiben auf alten Commits oder ein Shallow Clone kann das angegebene Objekt nicht finden. Die Ursache liegt normalerweise nicht bei Xcode, sondern darin, dass der Checkout nicht klar zwischen dem aktuellen Branch des Haupt-Repositorys und den darin gespeicherten Submodule-Commits unterscheidet.
Zuerst die Grenzen der Reproduzierbarkeit bestimmen
Ein Git Submodule speichert im Haupt-Repository einen Pfad, eine Remote-Adresse und einen exakten Commit. Das Ziel reproduzierbarer Builds besteht darin, für denselben Commit des Haupt-Repositorys immer dieselben Submodule-Objekte zu erhalten – und nicht bei jedem Lauf dem neuesten Stand eines Submodule-Branches zu folgen.
Erfassen Sie zunächst in der Entwicklungsumgebung eine Ausgangsbasis:
git submodule status --recursive
git config --file .gitmodules --get-regexp 'submodule\..*\.path'
git config --file .gitmodules --get-regexp 'submodule\..*\.url'
Ein - am Anfang einer Statuszeile bedeutet, dass das Submodule noch nicht initialisiert wurde. + weist darauf hin, dass der Commit im Arbeitsverzeichnis nicht mit dem im Haupt-Repository gespeicherten Commit übereinstimmt, und U kennzeichnet einen Merge-Konflikt. In der CI dürfen diese drei Zustände nicht als normales Ergebnis gelten.
Führen Sie in Build-Jobs nicht
git submodule update --remoteaus. Der Befehl folgt dem konfigurierten Branch, sodass derselbe Commit des Haupt-Repositorys zu unterschiedlichen Zeitpunkten unterschiedliche Abhängigkeiten beziehen kann.
Relative Adressen eignen sich für Repository-Verschiebungen innerhalb derselben Code-Hosting-Grenze, werden jedoch anhand der Remote-Adresse des Haupt-Repositorys aufgelöst. Wenn ein Job origin vorübergehend umschreibt, sollte er während der Initialisierung git submodule sync --recursive ausführen und die tatsächlich aufgelösten Adressen nach Entfernung vertraulicher Angaben zur Kontrolle ausgeben.
Einen einheitlichen Einstiegspunkt für den Checkout schaffen
Mischen Sie clone --recurse-submodules, manuelle Pull-Vorgänge in einzelnen Verzeichnissen und nachträgliche Reparaturbefehle im Build-Skript nicht miteinander. Die CI sollte zuerst das Haupt-Repository abrufen und anschließend alle Ebenen über einen einzigen Einstiegspunkt verarbeiten:
set -euo pipefail
git submodule sync --recursive
git submodule update --init --recursive --jobs 4
expected_file="$(mktemp)"
actual_file="$(mktemp)"
trap 'rm -f "$expected_file" "$actual_file"' EXIT
git ls-tree -r HEAD |
awk '$2 == "commit" { print $3, $4 }' |
sort > "$expected_file"
git submodule status --recursive |
sed -E 's/^[ +\-U]([0-9a-f]+) ([^ ]+).*/\1 \2/' |
sort > "$actual_file"
diff -u "$expected_file" "$actual_file"
--jobs 4 begrenzt hier lediglich die Anzahl paralleler Netzwerkanfragen und ist keine allgemeingültige Leistungsempfehlung. Da sich Knotennetzwerke, Remote-Limits und die Anzahl der Submodule unterscheiden, sollte mit einer niedrigen Parallelität begonnen und diese anschließend anhand der Fehlerrate angepasst werden.
Vor dem Build sollte das Skript außerdem git diff --submodule=log --exit-code ausführen. Wenn ein Installationsschritt unbemerkt das Arbeitsverzeichnis eines Submodules verändert, muss der Job sofort fehlschlagen, statt die Archivierung mit unbekanntem Quellcode fortzusetzen.
Shallow Clones mit Bedacht einsetzen
Shallow Clones reduzieren das Übertragungsvolumen, sind aber auch eine häufige Ursache für den Fall, dass ein Commit nachweislich auf dem Remote vorhanden ist, die CI ihn jedoch nicht findet. Das im Haupt-Repository gespeicherte Submodule-Objekt kann vor der aktuellen Shallow-Grenze liegen oder nur über den Verlauf eines inzwischen gelöschten Branches erreichbar sein.
| Szenario | Empfohlene Strategie | Vorgehen bei Fehlern |
|---|---|---|
| Submodule-Commit liegt nahe an der Branch-Spitze | Checkout mit begrenzter Tiefe verwenden | Nach Erhöhung der Tiefe erneut versuchen |
| Historischer Abstand ist nicht konstant | Submodule vollständig auschecken | Nicht auf Schätzungen mit fester Tiefe verlassen |
| Mehrstufig verschachtelte Submodule | Commits Ebene für Ebene verifizieren | Fehlerpfad und Objekt-Hash ausgeben |
| Remote-Objekt ist nicht mehr erreichbar | Repository-Referenzen korrigieren | Das Problem nicht durch Wechseln des Branches verdecken |
Wenn ein Shallow Clone beibehalten werden muss, versuchen Sie zunächst:
git submodule update --init --recursive --depth 50
Wechseln Sie nach einem Fehler nicht direkt zu --remote. Öffnen Sie stattdessen den betroffenen Pfad, führen Sie git fetch --deepen=100 aus und prüfen Sie anschließend mit git cat-file -e <commit>^{commit}. Ist das Objekt weiterhin nicht vorhanden, müssen die Aufbewahrung des Remote-Objekts oder der im Haupt-Repository gespeicherte Commit geprüft werden, statt weitere zufällige Wiederholungsversuche hinzuzufügen.
Zugangsdaten für private Submodule isolieren
Submodule können unterschiedlichen Berechtigungsgrenzen unterliegen. Am zuverlässigsten ist eine temporäre Git-Konfiguration pro Job, die nur für die Lebensdauer des Prozesses eingebunden wird und die globale Konfiguration des Knotens nicht verändert.
git_config="$(mktemp)"
chmod 600 "$git_config"
trap 'rm -f "$git_config"' EXIT
git config --file "$git_config" credential.helper ""
git config --file "$git_config" protocol.version 2
GIT_CONFIG_GLOBAL="$git_config" \
git submodule update --init --recursive
Authentifizierungswerte müssen über geschützte CI-Variablen oder kurzlebige Dateien bereitgestellt werden. Sie dürfen nicht in .gitmodules, Remote-Adressen, Befehlsargumente oder Build-Protokolle geschrieben werden. Prüfen Sie nach Abschluss des Jobs mindestens Folgendes:
git config --global --list
git remote -v
git submodule foreach --recursive 'git remote -v'
Vertrauliche Angaben müssen aus der Ausgabe entfernt werden, bevor sie geprüft wird. Wurden Zugangsdaten in eine URL eingefügt, reicht das Löschen temporärer Dateien nicht aus. Zusätzlich müssen die Remote-Adressen wiederhergestellt sowie der Shell-Verlauf, Diagnosepakete und Cache-Verzeichnisse kontrolliert werden.
Fehlerinformationen in verwertbare Diagnosebelege umwandeln
Bei einem Submodule-Fehler darf nicht nur die Meldung „Exit-Code 128“ erhalten bleiben. Protokolliert werden sollten der Commit des Haupt-Repositorys, der Fehlerpfad, das erwartete Objekt, das tatsächliche Objekt, der aufgelöste Hostname, die Checkout-Tiefe und die Fehlerphase – jedoch keine Benutzernamen, Token oder vollständigen Authentifizierungsadressen.
Die Reihenfolge der Diagnose kann fest vorgegeben werden:
- Mit
git ls-tree HEAD <path>das vom Haupt-Repository erwartete Objekt bestimmen. - Anhand von
.gitmodulesund der lokalen Git-Konfiguration die endgültige Adresse bestätigen. - Mit
git cat-file -eprüfen, ob das Objekt bereits lokal vorhanden ist. - Den Remote-Zugriff mit schreibgeschützter Authentifizierung testen.
- Die Shallow-Clone-Grenze und den Status verschachtelter Submodule prüfen.
- Das Arbeitsverzeichnis bereinigen und dasselbe Checkout-Skript erneut ausführen.
Archivieren Sie abschließend git submodule status --recursive als Build-Metadaten. Die Ausgabe ist klein, zeigt aber eindeutig, welche Abhängigkeits-Commits für diesen Build verwendet wurden. Jobs auf SDKMac-Knoten sollten demselben Prinzip folgen: zuerst die Quellcode-Eingaben fixieren und erst danach Xcode oder andere Toolchains starten, damit Checkout- und Kompilierungsfehler klar voneinander getrennt bleiben.
Häufig gestellte Fragen
Warum schlägt der Checkout trotz festem Submodule-Commit fehl?
Das Hauptrepository speichert nur die Objekt-ID. URL, Berechtigung, Klontiefe und die Verfügbarkeit des Objekts im entfernten Repository müssen zusätzlich geprüft werden.
Sollte die CI git submodule update --remote verwenden?
Nein, nicht für reproduzierbare Builds. Der Schalter folgt dem konfigurierten Branch und kann für denselben Commit des Hauptrepositories zu unterschiedlichen Abhängigkeiten führen.
Wie bleiben keine Zugangsdaten auf dem Cloud Mac zurück?
Zugangsdaten werden nur in einer auftragsspezifischen temporären Konfiguration bereitgestellt, per trap entfernt und anschließend in Git-Konfiguration, Remote-URLs und Protokollen kontrolliert.
Wählen Sie einen Cloud-Mac für kontinuierliche Builds
Vergleichen Sie Konfigurationen, Regionen und vier Abrechnungszeiträume von SDKMac M4 und SDKMac M4 Pro und erstellen Sie anschließend Ihre Bestellung.