Der Build auf dem Cloud-Mac läuft erfolgreich durch, doch nach dem Download auf das Auslieferungssystem sind Skripte plötzlich nicht mehr ausführbar, symbolische Links wurden zu regulären Dateien oder die Prüfung eines App-Bundles schlägt fehl. Solche Probleme entstehen häufig nicht beim Kompilieren, sondern erst beim abschließenden Packen und Entpacken. macOS-Dateien können neben Inhalt und Pfad auch erweiterte Attribute, Zugriffssteuerungslisten, Resource Forks und Beziehungen zwischen symbolischen Links enthalten. Prüft der Auslieferungsprozess lediglich, ob sich das Archiv öffnen lässt, gelangen entsprechende Veränderungen unbemerkt bis in die Veröffentlichung.
Zuerst den verlustfrei zu erhaltenden Umfang definieren
Komprimieren Sie nicht den gesamten Arbeitsbereich. Erstellen Sie zunächst ein Staging-Verzeichnis, das ausschließlich die auszuliefernden Inhalte enthält, und legen Sie ausdrücklich fest, welche Metadaten zum Artefaktvertrag gehören.
Typische Bestandteile sind App-Bundles, Frameworks, dynamische Bibliotheken, Kommandozeilenwerkzeuge, Konfigurationsvorlagen und Symboldateien. Cache-Verzeichnisse, temporäre Testdateien, Verzeichnisse der Versionsverwaltung und lokale Protokolle gehören nicht hinein. Ein Staging-Verzeichnis verhindert außerdem, dass Build-Aufgaben das Quellverzeichnis während des Packens weiter verändern.
set -euo pipefail
ROOT="$PWD"
SOURCE="$ROOT/build/Release"
STAGE="$ROOT/artifacts/stage"
ARCHIVE="$ROOT/artifacts/release.zip"
rm -rf "$STAGE"
mkdir -p "$STAGE"
ditto "$SOURCE/MyProduct.app" "$STAGE/MyProduct.app"
ditto "$SOURCE/MyFramework.framework" "$STAGE/MyFramework.framework"
Bei der ersten Prüfung sollten die Anzahlen regulärer Dateien, Verzeichnisse und symbolischer Links erfasst werden. Gerade symbolische Links werden bei der Übergabe zwischen verschiedenen Werkzeugen leicht aufgelöst. Dadurch kann sich die Archivgröße ändern oder ein relatives Linkziel ungültig werden.
find "$STAGE" -type f | wc -l
find "$STAGE" -type d | wc -l
find "$STAGE" -type l -print
macOS-Metadaten vor dem Packen erfassen
Mit ls -le@ lassen sich Berechtigungen, ACLs und erweiterte Attribute prüfen. xattr gibt die Attribute wichtiger Objekte im Detail aus. Behandeln Sie nicht jedes unbekannte Attribut pauschal als überflüssig.
ls -le@ "$STAGE"
xattr -lr "$STAGE" > "$ROOT/artifacts/xattr-before.txt"
find "$STAGE" -type l -exec sh -c '
for item do
printf "%s -> %s\n" "$item" "$(readlink "$item")"
done
' sh {} + > "$ROOT/artifacts/symlinks-before.txt"
Verwenden Sie
xattr -crnicht als universelle Reparaturmaßnahme. Eine rekursive Bereinigung kann zwar bestimmte oberflächliche Fehler vorübergehend beseitigen, entfernt aber zugleich wichtige Informationen für die Ursachenanalyse. Erfassen Sie zuerst den Bestand und bestimmen Sie die Herkunft. Entfernen Sie anschließend nur einzelne Attribute, die nachweislich nicht benötigt werden.
Prüfen Sie außerdem die Ausführungsbits ausführbarer Dateien. Ein Skript, das im Repository ausführbar ist, muss diese Berechtigung nach dem Kopieren in das Staging-Verzeichnis nicht zwingend behalten. Mit find "$STAGE" -type f -perm -111 können Sie einen Ausgangszustand protokollieren. Muss ein bestimmtes Skript ausführbar sein, gehört diese Anforderung in das Abnahmeskript und sollte nicht vom Gedächtnis einzelner Personen abhängen.
Ein auslieferbares Archiv mit ditto erstellen
ditto gehört zum Lieferumfang von macOS und eignet sich für App-Bundles und Framework-Verzeichnisse. Beim Erstellen des ZIP-Archivs sollten Ressourcen und erweiterte Attribute erhalten bleiben. Wird außerdem die Ebene des Staging-Verzeichnisses beibehalten, erhält die empfangende Seite eine stabile Verzeichnisstruktur auf oberster Ebene.
rm -f "$ARCHIVE"
ditto -c -k \
--sequesterRsrc \
--keepParent \
"$STAGE" \
"$ARCHIVE"
shasum -a 256 "$ARCHIVE" > "$ARCHIVE.sha256"
du -h "$ARCHIVE"
Sowohl der Archivname als auch das oberste Verzeichnis im Archiv sollten unveränderlich sein. Zeitstempel, Rechnernamen oder absolute Pfade temporärer Arbeitsbereiche gehören nicht in Verzeichnisnamen. Andernfalls müssen nachgelagerte Skripte den Einstiegspunkt erraten. Benötigt die Pipeline eine eindeutige Kennung, kann die Commit-ID in den externen Artefaktdatensatz aufgenommen werden, ohne die Struktur innerhalb des Archivs zu verändern.
Bei Dateien aus nicht kontrollierten Quellen sollten Sie vor der Übernahme in das Staging-Verzeichnis außerdem nach Zeilenumbrüchen, unsichtbaren Zeichen und ungewöhnlichen Unicode-Kombinationen in Dateinamen suchen. Normale Leerzeichen lassen sich sicher verarbeiten, sofern Variablen in Skripten konsequent in doppelte Anführungszeichen gesetzt werden, damit die Shell Pfade nicht aufteilt.
In ein separates Verzeichnis entpacken und vergleichen
Zur Abnahme darf das Archiv nicht über das ursprüngliche Verzeichnis entpackt werden. Fehlende Dateien könnten sonst durch ältere Dateien verdeckt werden. Verwenden Sie für jeden Durchlauf ein vollständig neues Verzeichnis und lassen Sie das Archiv von ditto entpacken.
VERIFY="$ROOT/artifacts/verify"
rm -rf "$VERIFY"
mkdir -p "$VERIFY"
ditto -x -k "$ARCHIVE" "$VERIFY"
test -d "$VERIFY/stage/MyProduct.app"
test -d "$VERIFY/stage/MyFramework.framework"
find "$VERIFY/stage" -type l -exec sh -c '
for item do
printf "%s -> %s\n" "$item" "$(readlink "$item")"
done
' sh {} + > "$ROOT/artifacts/symlinks-after.txt"
diff -u \
"$ROOT/artifacts/symlinks-before.txt" \
"$ROOT/artifacts/symlinks-after.txt"
Für die Inhaltsprüfung sollten sowohl vor dem Packen als auch nach dem Entpacken Dateilisten erzeugt werden. Da Pfade Leerzeichen enthalten können, sind Schleifen mit nicht in Anführungszeichen gesetzten Variablen ungeeignet. Das folgende Python-Fragment sortiert nach relativen Pfaden und berechnet für reguläre Dateien jeweils einen SHA-256-Hash.
from hashlib import sha256
from pathlib import Path
import sys
root = Path(sys.argv[1])
for path in sorted(root.rglob("*"), key=lambda p: str(p.relative_to(root))):
if path.is_file() and not path.is_symlink():
digest = sha256(path.read_bytes()).hexdigest()
print(digest, path.relative_to(root))
Führen Sie das Skript einmal für stage und einmal für verify/stage aus und vergleichen Sie die Ausgaben anschließend mit diff. So werden veränderte Inhalte, fehlende Dateien und unerwartete Ergänzungen gleichermaßen erkannt.
Struktur- und Signaturprüfungen als Freigabesperre einsetzen
Enthält das Artefakt bereits signierte App-Bundles oder Frameworks, muss die Signatur nach dem Entpacken erneut geprüft werden. Eine einzige Prüfung vor dem Komprimieren reicht nicht aus, da das Archivierungswerkzeug selbst Fehler verursachen kann.
codesign --verify --deep --strict \
"$VERIFY/stage/MyProduct.app"
codesign -d --verbose=4 \
"$VERIFY/stage/MyProduct.app" 2>&1 |
sed -n '1,20p'
--deep eignet sich für die abschließende Integritätsprüfung, ersetzt jedoch keine schrittweise Eingrenzung. Schlägt die Prüfung fehl, ist zunächst festzustellen, ob die Hauptanwendung, ein eingebettetes Framework oder eine ausführbare Hilfsdatei betroffen ist. Führen Sie danach codesign --verify --strict --verbose=4 für den konkreten Pfad aus. Vergleichen Sie gleichzeitig die xattr-Ausgaben vor und nach dem Packen, um zu klären, ob die Veränderung beim Archivieren entstanden ist.
Als feste Abbruchkriterien für die Abnahme empfehlen sich: eine abweichende Verzeichnisstruktur auf oberster Ebene, unterschiedliche Dateilisten, veränderte Ziele symbolischer Links, fehlende erforderliche Ausführungsbits, auffällige Änderungen wichtiger erweiterter Attribute oder eine fehlgeschlagene Signaturprüfung. Sobald eines dieser Kriterien erfüllt ist, muss die Veröffentlichung gestoppt werden. Das Archiv einfach erneut zu packen und auf ein anderes Ergebnis zu hoffen, ist keine Lösung.
Häufige Fehler und Auslieferungscheckliste
Der häufigste Fehler ist die Kombination mehrerer Werkzeuge: Zuerst erstellt ein Skript ein gewöhnliches ZIP-Archiv, anschließend entpackt und komprimiert das Upload-Programm es automatisch erneut. Je länger diese Verarbeitungskette wird, desto schwieriger lassen sich die Bedeutungen der Metadaten nachvollziehen. Legen Sie ein einziges Packwerkzeug fest und übergeben Sie das unveränderte Originalarchiv direkt an den Artefaktspeicher.
Ein zweiter häufiger Fehler besteht darin, gleichzeitig zu bauen und zu packen. Solange der Compiler noch Dateien schreibt, können Dateiliste und Archiv unterschiedliche Zeitstände abbilden. Warten Sie stattdessen, bis der Build-Prozess erfolgreich beendet wurde, kopieren Sie das Ergebnis in ein schreibgeschütztes Staging-Verzeichnis und beginnen Sie erst danach mit der Archivierung.
Der dritte Fehler ist, ausschließlich auf demselben Rechner zu testen. Löschen Sie mindestens das alte Abnahmeverzeichnis, entpacken Sie das Archiv erneut und starten Sie die Prüfungen über die Pfade innerhalb des Archivs. Wenn möglich, sollte ein unabhängiger Auftrag dieselben Skripte zusätzlich ausführen. Lang laufende Pipelines auf BerryVM können Bestandsdateien, Archiv-Hashes und Abnahmeprotokolle als zusammengehörigen Auslieferungsdatensatz speichern. Die Protokolle dürfen jedoch weder Zugangsdaten noch unbereinigte Umgebungsvariablen enthalten.
Die abschließende Checkliste sollte kurz bleiben: Das Staging-Verzeichnis ist eingefroren, symbolische Links sind erfasst, erweiterte Attribute wurden inventarisiert, der Archiv-Hash wurde erzeugt, das Archiv wurde separat entpackt, die Dateilisten stimmen überein, die Ausführungsbits entsprechen den Vorgaben und alle erforderlichen Signaturprüfungen sind erfolgreich. Damit ist das Packen kein beiläufiger Schritt nach dem Build mehr, sondern eine reproduzierbare technische Freigabesperre, die bei Abweichungen zuverlässig fehlschlägt.
Häufig gestellte Fragen
Warum reicht zip -r für macOS-Build-Artefakte nicht immer aus?
Ein gewöhnlicher ZIP-Ablauf kann Ressourcen-Forks, erweiterte Attribute, ACLs oder die Semantik symbolischer Links verlieren. Für App-Bundles und Frameworks ist ditto die robustere Wahl.
Sollte man vor dem Packen pauschal xattr -cr ausführen?
Nein. Attribute sollten zuerst erfasst und bewertet werden. Eine rekursive Löschung kann Informationen entfernen, die für die Auslieferung, Prüfung oder spätere Fehleranalyse benötigt werden.
Genügt der SHA-256-Hash des Archivs als Abnahme?
Nein. Er belegt nur, dass sich das Archiv nicht verändert hat. Zusätzlich müssen entpackte Struktur, Symlinks, Attribute und bei signierten Bundles die Codesign-Prüfung kontrolliert werden.
Verlagere deine nächste Entwicklungsaufgabe auf einen Cloud-Mac
Wähle Berry M4 oder Berry M4 Pro, prüfe die fünf Standorte, die Mietdauer und Zusatzoptionen und gib anschließend die Bestellung auf.