Disk-Images in Cloud-Mac-CI zuverlässig einhängen und bereinigen

Disk-Images in Cloud-Mac-CI zuverlässig einhängen und bereinigen

CI-Jobs müssen häufig DMG-Dateien einhängen, etwa um das Layout eines Installationspakets zu prüfen, Testdaten einzulesen, das signierte Auslieferungsverzeichnis zu validieren oder den Inhalt zweier Disk-Images zu vergleichen. Bei einer einzelnen manuellen Ausführung treten meist keine Probleme auf. Kritisch wird es erst, wenn Jobs parallel laufen, Skripte unerwartet beendet werden oder ein vorheriger Job ein eingehängtes Volume zurücklässt. Ein Skript, das fest auf /Volumes/Product zugreift, kann dann unbemerkt die Daten eines anderen Jobs lesen, anstatt sofort mit einem Fehler abzubrechen.

Warum das standardmäßige Einhängen bei parallelen Jobs außer Kontrolle gerät

hdiutil attach App.dmg wählt den Namen des Mountpunkts anhand der im Image hinterlegten Volume-Bezeichnung. Ist bereits ein Volume mit demselben Namen vorhanden, kann macOS einen neuen, nummerierten Pfad anlegen. Greift das Skript weiterhin auf das ursprüngliche feste Verzeichnis zu, sind die Testergebnisse nicht mehr verlässlich.

Ein weiteres häufiges Problem besteht darin, detach nur im Erfolgsfall auszuführen. Sobald ein Prüfbefehl fehlschlägt, der Job beendet wird oder ein nachfolgender Schritt vorzeitig zurückkehrt, wird die Bereinigung nicht mehr ausgeführt. Längerfristig wiederverwendete Cloud-Mac-Knoten sammeln dadurch nach und nach zurückgelassene Volumes an. Die Folgen sind Konflikte bei Volume-Namen, belegte Dateien oder eine fehlerhafte Erkennung des Arbeitsverzeichnisses.

Ein Mountpunkt sollte als temporäre Ressource des jeweiligen Jobs behandelt werden, nicht als gemeinsam genutzter Pfad des Rechners. Erstellung, Verwendung, Aushängen und Diagnose müssen in der Verantwortung desselben Jobs liegen.

Vor der Umstellung sollte zunächst der aktuelle Zustand protokolliert werden:

/usr/bin/hdiutil info
/bin/df -h
/bin/ls -la /Volumes

Mit hdiutil info lässt sich die Zuordnung zwischen Images, Geräten und Mountpfaden prüfen. df zeigt dagegen lediglich die Belegung der Dateisysteme und ersetzt keine Kontrolle der Image-Zuordnungen.

Jedem Job einen eindeutigen Mountpunkt zuweisen

Das Mountverzeichnis sollte eine stabile und eindeutige Job-ID enthalten. Der Repository-Name allein reicht nicht aus, da für dasselbe Repository mehrere Branches oder Wiederholungsjobs gleichzeitig ausgeführt werden können. Außerdem müssen Zeichen wie Schrägstriche und Leerzeichen aus der Kennung herausgefiltert werden.

Das folgende Skript überprüft zunächst das Image, hängt es anschließend schreibgeschützt ein und versucht sowohl bei regulärem Abschluss als auch bei Befehlsfehlern oder Abbruchsignalen, das Volume wieder auszuhängen:

#!/bin/bash
set -euo pipefail

IMAGE="${1:?usage: mount-image.sh path/to/file.dmg}"
RAW_JOB_ID="${CI_JOB_ID:-local-$$}"
JOB_ID="${RAW_JOB_ID//[^a-zA-Z0-9._-]/_}"
MOUNT="${TMPDIR%/}/sdkmac-image-${JOB_ID}"
ATTACHED=0

cleanup() {
  local status=$?
  trap - EXIT INT TERM

  if (( ATTACHED == 1 )); then
    if ! /usr/bin/hdiutil detach "$MOUNT"; then
      printf 'unable to detach %s\n' "$MOUNT" >&2
    fi
  fi

  /bin/rmdir "$MOUNT" 2>/dev/null || true
  exit "$status"
}

trap cleanup EXIT INT TERM

/usr/bin/hdiutil verify "$IMAGE"
/bin/mkdir -p "$MOUNT"
/usr/bin/hdiutil attach \
  -nobrowse \
  -readonly \
  -mountpoint "$MOUNT" \
  "$IMAGE"
ATTACHED=1

test -r "$MOUNT"
/usr/bin/find "$MOUNT" -maxdepth 2 -type f -print

-nobrowse verhindert, dass das eingehängte Volume in den üblichen Navigationsablauf der grafischen Oberfläche aufgenommen wird. -readonly schützt die Testdaten davor, durch Prüfschritte versehentlich verändert zu werden. Das Skript verwendet rmdir statt rm -rf: Falls das Aushängen fehlschlägt, löscht rmdir keine Inhalte innerhalb eines weiterhin eingehängten Verzeichnisses.

Schreibschutz ist nicht für jeden Anwendungsfall geeignet

Wenn Schreibvorgänge getestet werden müssen, sollte ein gemeinsam genutztes Referenz-Image nicht einfach beschreibbar eingehängt werden. Stattdessen wird das Image zunächst für den aktuellen Job kopiert. Erst nachdem sichergestellt wurde, dass die Kopie in einem jobspezifischen temporären Verzeichnis liegt, wird sie eingehängt. So kann ein fehlgeschlagener Job die Referenzdaten für den nächsten Lauf nicht verunreinigen.

Verwendungszweck Empfohlener Modus Wichtigste Abnahmekriterien
Installationspaket oder Testdaten prüfen Schreibgeschützt einhängen Vorhandensein der Dateien, Berechtigungen und Prüfsummen
Schreibablauf validieren Jobspezifische beschreibbare Kopie Schreibergebnis und vollständiges Aushängen
Zwei Images vergleichen Zwei unabhängige schreibgeschützte Mountpunkte Eindeutige Pfade und feste Vergleichsreihenfolge

Diagnoseinformationen bei fehlgeschlagenem Aushängen sichern

Wenn das Aushängen fehlschlägt, liegt dies normalerweise nicht an einem zufälligen Fehler von hdiutil. Meist hält ein Prozess sein aktuelles Verzeichnis, eine geöffnete Datei oder einen Arbeitspfad weiterhin innerhalb des Volumes. Statt die Ursache sofort durch erzwungenes Aushängen zu verdecken, sollten zunächst die blockierenden Prozesse ermittelt werden:

/usr/sbin/lsof +D "$MOUNT" 2>/dev/null || true
/usr/bin/hdiutil info
/bin/ps -axo pid,ppid,command

lsof +D kann bei großen Verzeichnissen langsam sein und sollte daher nur im Fehlerfall ausgeführt werden. Besonders zu prüfen sind Testprozesse, Log-Sammler, Komprimierungswerkzeuge sowie Shells, die per cd in das Mountverzeichnis gewechselt und anschließend nicht wieder herausgewechselt sind. Üblicherweise lässt sich das Problem beheben, indem auf das Ende von Unterprozessen gewartet, offene Datei-Handles geschlossen und vor dem Aushängen ausdrücklich in das Arbeitsverzeichnis des Jobs zurückgewechselt wird.

Die Bereinigungsfunktion sollte außerdem den ursprünglichen Exit-Code beibehalten. Überschreibt ein Fehler beim Aushängen den eigentlichen Testfehler, zeigt die Pipeline nur noch die Bereinigungsstörung an, während die ursprüngliche Fehlerursache verloren geht. Robuster ist es, Teststatus und Bereinigungsstatus getrennt zu speichern und beide Werte separat in die Job-Zusammenfassung zu schreiben.

Verwaiste Volumes kontrollieren statt blind zu löschen

Vor dem Start eines neuen Jobs kann ein Knoten nach Mountverzeichnissen suchen, die nach dem Namensschema der eigenen Pipeline angelegt wurden. Ein ähnlich aussehender Name allein ist jedoch kein ausreichender Grund zum Löschen. Zunächst muss bestätigt werden, dass das Verzeichnis tatsächlich ein Mountpunkt ist. Anschließend ist zu prüfen, ob der zugehörige Job noch läuft und ob das Image zum aktuellen Arbeitsbereich gehört.

Die Prüfergebnisse sollten in drei Kategorien eingeteilt werden:

  1. Volumes des aktuellen Jobs: Sie werden über den Exit-Trap regulär ausgehängt.
  2. Volumes anderer aktiver Jobs: Sie werden protokolliert und übersprungen; eine jobübergreifende Bereinigung ist unzulässig.
  3. Möglicherweise verwaiste Volumes ohne ermittelbaren aktiven Job: hdiutil info, blockierende Prozesse und Erstellungskontext werden erfasst, bevor das Volume nach Bestätigung ausgehängt wird.

Erzwungenes Aushängen darf nur als letztes Mittel nach manueller Bestätigung eingesetzt werden. Ein automatisches Skript, das allein nach dem Alter eines Verzeichnisses entscheidet, kann einen lang laufenden, aber weiterhin ordnungsgemäß arbeitenden Job unterbrechen. Zudem entspricht der Verzeichniszeitstempel nicht dem Zeitpunkt des Einhängens: Auch das Kopieren von Dateien oder das Lesen von Metadaten kann die zugrunde liegenden Zeitangaben verändern.

Image-Verarbeitung in die Pipeline-Abnahme aufnehmen

Ein wiederverwendbarer Ablauf für Disk-Images sollte mindestens folgende Punkte prüfen:

Auf den dedizierten physischen Mac-Knoten von SDKMac eignet sich dieses Vorgehen gleichermaßen für interaktive Fehleranalysen und unbeaufsichtigte Pipelines. Entscheidend ist nicht, immer mehr Bereinigungsbefehle hinzuzufügen, sondern für jedes eingehängte Volume einen eindeutigen Besitzer, einen klaren Lebenszyklus und verwertbare Fehlernachweise festzulegen. Erst dann führen wiederverwendete Rechner oder parallele Ausführungen bei DMG-Prüfungen nicht mehr zu Ergebnissen, die sich nur schwer reproduzieren lassen.

Häufig gestellte Fragen

Warum sollte ein CI-Job nicht den automatisch erzeugten Volume-Namen verwenden?

Parallele Jobs können Images mit demselben Volume-Namen öffnen. macOS ergänzt dann Nummern, wodurch ein fest codierter Pfad auf das falsche Volume zeigen kann.

Darf der Mountordner nach einem fehlgeschlagenen detach gelöscht werden?

Nein. Zuerst müssen belegende Prozesse ermittelt und das Volume regulär ausgehängt werden. Ein rekursives Löschen eines noch eingebundenen Pfads kann Daten im Image verändern.

Wann ist ein schreibgeschützter Mount sinnvoll?

Für Paketprüfungen, Testdaten und unveränderliche Artefakte sollte er der Standard sein. Schreibzugriff ist nur für ausdrücklich veränderbare, jobspezifische Kopien nötig.

Exklusiver physischer Knoten

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.

Mietplan auswählen