Der Build auf dem Cloud-Mac ist erfolgreich, und auch das Archiv lässt sich entpacken. Trotzdem können beim Empfänger Dateien wie ._Config.json oder .DS_Store auftauchen; ebenso können beim Upload unerklärliche erweiterte Attribute Probleme verursachen. Die Ursache liegt meist nicht beim Compiler, sondern im Umgang von Finder-Aktionen, Kopiervorgängen zwischen verschiedenen Dateisystemen und Archivierungswerkzeugen mit macOS-Metadaten. Statt den Arbeitsbereich pauschal zu bereinigen, sollte der Auslieferungsprozess in vier überprüfbare Schritte gegliedert werden: Staging, Scannen, Archivieren und erneutes Validieren.
Zulässige Inhalte des Auslieferungspakets festlegen
macOS-Dateien können neben ihrem Namen und Inhalt auch erweiterte Attribute, Resource Forks und Finder-Metadaten enthalten. Im lokalen Dateisystem sind diese nicht unbedingt problematisch. Nach dem Übertragen in ZIP-Archive, auf Netzlaufwerke oder in Objektspeicher können daraus jedoch AppleDouble-Begleitdateien entstehen.
Legen Sie deshalb zuerst Regeln für die Artefakte fest, anstatt Dateien erst während der Archivierung zu löschen:
| Objekt | Standardrichtlinie | Grund |
|---|---|---|
.DS_Store |
Ablehnen | Beschreibt ausschließlich die Finder-Ordneransicht |
._* |
Ablehnen | Entsteht häufig bei der Konvertierung von Resource Forks zwischen Dateisystemen |
com.apple.quarantine |
Prüfen und gezielt behandeln | Kann von Downloadwerkzeugen oder Browsern stammen |
com.apple.ResourceFork |
Nach Artefakttyp entscheiden | Für gewöhnliche Konfigurationspakete meist unnötig, für bestimmte Dateien jedoch möglicherweise erforderlich |
| Signierte App-Bundles | Struktur beibehalten und erneut validieren | Eine undifferenzierte Bereinigung erhöht das Risiko von Signaturfehlern |
Bereinigt werden sollte ein separates Staging-Verzeichnis, nicht der Quellcode-Arbeitsbereich. Selbst eine fehlerhafte Richtlinie verändert dann weder Caches noch Abhängigkeitsverzeichnisse oder Eingaben, die für den nächsten Build benötigt werden.
Enthält die Auslieferung eine signierte App, muss zunächst die Signatur des ursprünglichen Artefakts geprüft werden. Für gewöhnliche Dokumente, Konfigurationen und Protokolle kann eine strengere Richtlinie zur Ablehnung von Metadaten gelten. Beide Objektklassen sollten nicht mit demselben rekursiven Löschbefehl behandelt werden.
Ein temporäres, isoliertes Staging-Verzeichnis erstellen
Das Staging-Verzeichnis muss exklusiv für den aktuellen Auftrag angelegt und beim Beenden wieder entfernt werden. Verwenden Sie kein festes Verzeichnis wie /tmp/release, da parallele Aufträge sich sonst gegenseitig überschreiben können.
#!/bin/bash
set -euo pipefail
SOURCE="${1:?source path required}"
OUTPUT="${2:?output path required}"
STAGE="$(mktemp -d "${TMPDIR:-/tmp}/bamini-artifact.XXXXXX")"
UNPACK="$(mktemp -d "${TMPDIR:-/tmp}/bamini-unpack.XXXXXX")"
cleanup() {
rm -rf "$STAGE" "$UNPACK"
}
trap cleanup EXIT
/usr/bin/ditto --norsrc "$SOURCE" "$STAGE/payload"
find "$STAGE/payload" -type f -name '.DS_Store' -delete
find "$STAGE/payload" -type f -name '._*' -delete
Mit mktemp erhält jeder Auftrag ein eigenes Verzeichnis. trap stellt sicher, dass die Bereinigung sowohl bei Erfolg und Fehlern als auch bei Unterbrechungen erfolgt. ditto --norsrc kopiert hier die Inhalte, ohne Resource Forks aktiv zu übernehmen. Ein anschließender Scan bleibt dennoch erforderlich, da das Eingabeverzeichnis bereits materialisierte ._-Dateien enthalten kann.
Wenn der Workflow vollständige App-Bundles ausliefern soll, muss zunächst geprüft werden, ob die gewählte Kopierstrategie alle von der App benötigten Inhalte erhält. Sicherer ist es, App-Bundles und gewöhnliche Anhänge getrennt bereitzustellen und jeweils eigene Richtlinien anzuwenden.
Erweiterte Attribute scannen, statt sie pauschal zu löschen
xattr -cr ist bequem, aber zu undifferenziert. Der Befehl entfernt sowohl bekannte Quarantäneattribute als auch andere Attribute, die noch nicht von den Prüfregeln erfasst werden. Die Pipeline wird dadurch zwar „grün“, die eigentliche Ursache bleibt jedoch verborgen.
Geben Sie zunächst die Attributnamen aus und lassen Sie den Auftrag bei ausdrücklich verbotenen Einträgen fehlschlagen:
ATTR_REPORT="$STAGE/xattr.txt"
if /usr/bin/xattr -lr "$STAGE/payload" >"$ATTR_REPORT" 2>&1; then
if grep -E 'com\.apple\.(quarantine|ResourceFork)' "$ATTR_REPORT"; then
echo "disallowed macOS metadata found" >&2
exit 41
fi
fi
if find "$STAGE/payload" -type f \( -name '.DS_Store' -o -name '._*' \) -print -quit |
grep -q .; then
echo "hidden metadata files found" >&2
exit 42
fi
Diese Prüfung folgt dem Prinzip „gefunden bedeutet fehlgeschlagen“ und eignet sich dazu, zunächst den Schritt zu ermitteln, durch den die Metadaten eingebracht werden. Sobald die Quelle bekannt ist, kann sie gezielt beim Herunterladen, Kopieren oder Erzeugen korrigiert werden. Ist ein bestimmtes erweitertes Attribut tatsächlich zulässig, sollten Dateibereich und Attributname ausdrücklich in eine Positivliste aufgenommen werden, anstatt das gesamte Verzeichnis freizugeben.
Entstehungsschritt der Metadaten protokollieren
Scannen Sie jeweils nach dem Auschecken des Quellcodes, nach dem Build und nach dem Staging. Die erste Phase, in der eine Abweichung auftritt, bildet die Grenze für die weitere Fehlersuche. Häufige Ursachen sind das Öffnen von Verzeichnissen in grafischen Oberflächen, Kopiervorgänge von nicht nativen Dateisystemen sowie Werkzeuge, die Dateien zunächst herunterladen und anschließend entpacken. Ein Scan ausschließlich vor der endgültigen Archivierung kann Verunreinigungen zwar blockieren, weist jedoch nicht auf den verantwortlichen Prozessschritt hin.
Das Archiv nach dem Erstellen erneut öffnen und prüfen
Ein sauberes Staging-Verzeichnis garantiert nicht, dass das Archivierungswerkzeug keine neuen Metadaten schreibt. Nach dem Erstellen der ZIP-Datei sollten deshalb zunächst die enthaltenen Pfade geprüft und das Archiv anschließend in ein neues Verzeichnis entpackt und erneut validiert werden.
rm -f "$OUTPUT"
/usr/bin/ditto -c -k --norsrc --keepParent \
"$STAGE/payload" "$OUTPUT"
/usr/bin/unzip -Z1 "$OUTPUT" >"$STAGE/members.txt"
if grep -E '(^|/)\.DS_Store$|(^|/)\._[^/]+$' "$STAGE/members.txt"; then
echo "archive contains forbidden metadata files" >&2
exit 43
fi
/usr/bin/ditto -x -k "$OUTPUT" "$UNPACK"
if find "$UNPACK" -type f \( -name '.DS_Store' -o -name '._*' \) -print -quit |
grep -q .; then
echo "extracted artifact failed metadata check" >&2
exit 44
fi
Die Prüfung der Mitgliederliste ist schnell und eignet sich als erste Kontrollstufe. Die erneute Validierung nach dem Entpacken deckt zusätzlich Pfadkonvertierungen und das Verhalten des Entpackwerkzeugs ab. Beide Prüfungen sind erforderlich: Das Uploadsystem empfängt die Archivdatei, während die Benutzer letztlich mit dem entpackten Ergebnis arbeiten.
Enthält das Paket eine signierte App, suchen Sie nach dem Entpacken das zugehörige .app-Bundle und führen Sie erneut eine strenge Signaturprüfung aus. Es genügt nicht, nur die Kopie vor dem Archivieren zu prüfen, da Veränderungen durch die Archivierungsparameter sonst unentdeckt bleiben.
Die Prüfungen in einen reproduzierbaren Auslieferungsprozess integrieren
Das Skript sollte fest mit den beiden Parametern „Eingabeverzeichnis“ und „Ausgabedatei“ arbeiten. Jeder Auftrag sollte außerdem die folgenden Nachweise erzeugen:
- Pfad und Typ des Artefakts vor dem Staging.
- Ergebnis des Scans auf erweiterte Attribute.
- Mitgliederliste des ZIP-Archivs.
- Ergebnis der Prüfung auf versteckte Dateien nach dem Entpacken.
- Ergebnis der zweiten Signaturprüfung, sofern ein App-Bundle vorhanden ist.
- SHA-256-Prüfsumme des endgültigen Archivs.
Die Prüfsumme lässt sich mit shasum -a 256 artifact.zip erzeugen. Sie beweist nicht, dass zwei ZIP-Archive semantisch identische Inhalte besitzen, bestätigt jedoch, dass sich die Bytes während Upload, Download und Übergabe nicht verändert haben.
Wenn solche Aufträge auf einem Cloud-Mac von BAMini ausgeführt werden, prüfen Sie zunächst in der Konsole die aktuell verfügbaren Konfigurationen. Legen Sie das Skript anschließend im Repository ab und lassen Sie es von der CI aufrufen, statt sich darauf zu verlassen, dass ein Teammitglied eine manuelle Bereinigung durchführt. Bei einer fehlgeschlagenen Prüfung sollten die Mitgliederliste und der Attributbericht aufbewahrt, temporäre Verzeichnisse mit Projektinhalten jedoch gelöscht werden. Der nächste Auftrag muss ein neues Staging-Verzeichnis anlegen, damit Rückstände aus dem vorherigen Lauf das Ergebnis nicht verfälschen.
Das Ziel besteht nicht darin, sämtliche erweiterten Attribute zu entfernen. Das Auslieferungspaket soll ausschließlich Inhalte enthalten, die das Team ausdrücklich freigegeben hat. Das isolierte Staging begrenzt den Umfang von Änderungen, phasenweise Scans lokalisieren die Quelle, und die erneute Prüfung nach dem Entpacken validiert das tatsächlich ausgelieferte Ergebnis. Nur wenn alle drei Maßnahmen zusammenwirken, tauchen versteckte Metadaten nicht über unterschiedliche Kopierpfade immer wieder auf.
Häufig gestellte Fragen
Sollte xattr -cr im gesamten Arbeitsverzeichnis ausgeführt werden?
Nein. Der Befehl entfernt rekursiv alle erweiterten Attribute, kann benötigte Resource Forks zerstören und Fehler vorgelagerter Schritte verdecken. Bereinigen Sie nur ein isoliertes Staging nach Positivliste.
Warum bleiben nach dem Löschen von .DS_Store noch ._-Dateien übrig?
._-Dateien sind AppleDouble-Begleitdateien und keine Finder-Datenbanken. Deaktivieren Sie Resource Forks beim Archivieren und prüfen Sie anschließend die vollständige Mitgliederliste.
Wann sollte eine signierte Anwendung geprüft werden?
Prüfen Sie zuerst die Quellanwendung und kopieren Sie sie danach ins Staging. Entpacken Sie das fertige Archiv in ein neues Verzeichnis und führen Sie dort erneut eine strenge Signaturprüfung aus.
Den nächsten Build auf einen Mac mini in der Cloud verlagern
Wählen Sie Konfiguration, Abrechnungszeitraum und Knotenregion und führen Sie Entwicklungs-, Build- und Automatisierungsaufgaben auf einem dedizierten physischen Rechner aus.