Wenn Teams Befehlszeilentools, Hintergrundagenten oder interne Unternehmensanwendungen auf mehreren Macs bereitstellen, führt das direkte Kopieren von Dateien schnell zu drei Problemen: uneinheitliche Zielpfade, zurückbleibende Altdateien bei Upgrades und nicht nachvollziehbare Installationsergebnisse. Ein macOS PKG bündelt Payload, Berechtigungen, Installationsskripte und Versionsbelege in einem nachvollziehbaren Artefakt. Voraussetzung dafür ist jedoch, dass nicht einfach der Entwicklungs-Workspace an pkgbuild übergeben wird. Im Folgenden entsteht anhand einer unter /Library/Application Support/AcmeTool installierten Befehlszeilenkomponente ein Build- und Abnahmeprozess, der sich in eine Cloud-Mac-CI integrieren lässt.
Installationsvertrag zuerst festlegen
Vor dem Paketieren müssen Zielpfad, Eigentümer, Berechtigungen, Paketkennung und Upgrade-Regeln eindeutig festgelegt werden. Die Paketkennung sollte nach der Veröffentlichung unverändert bleiben, während die Version monoton steigen muss. Andernfalls können die Systembelege nicht zuverlässig bestimmen, welche Installation eine andere ersetzt.
| Element | Beispiel | Abnahmekriterium |
|---|---|---|
| Installationsstammverzeichnis | /Library/Application Support/AcmeTool |
Keine Dateien im Benutzerordner |
| Ausführbare Datei | bin/acmetool |
root:wheel, Berechtigung 0755 |
| Konfigurationsvorlage | config/default.json |
Berechtigung 0644, enthält keine Schlüssel |
| Paketkennung | com.example.acmetool.pkg |
Ändert sich nicht je nach Branch |
| Version | 1.4.0 |
Entspricht dem Release-Tag |
Das Projektverzeichnis auf dem Build-Rechner gehört nicht zum Installationsvertrag. .git, Testberichte, Download-Caches, temporäre Protokolle und lokal erzeugte erweiterte Attribute dürfen nicht in das PKG gelangen.
Die zuverlässigste Eingabe für die Paketerstellung ist nicht das „aktuelle Repository“, sondern ein neu erstelltes Staging-Verzeichnis mit vollständig erfassbarem Inhalt.
Saubere Payload bereitstellen
Zu Beginn jedes Jobs wird der alte Staging-Bereich gelöscht. Anschließend werden ausschließlich ausdrücklich freigegebene Dateien kopiert und die Berechtigungen vereinheitlicht. Ein pauschales cp -R . ist zu vermeiden, da neu hinzugekommene Dateien sonst unbemerkt in das Installationspaket gelangen können.
set -euo pipefail
ROOT="$PWD"
STAGE="$ROOT/build/stage"
PKGDIR="$ROOT/build/packages"
INSTALL_DIR="$STAGE/Library/Application Support/AcmeTool"
rm -rf "$STAGE" "$PKGDIR"
mkdir -p "$INSTALL_DIR/bin" "$INSTALL_DIR/config" "$PKGDIR"
install -m 0755 "$ROOT/dist/acmetool" "$INSTALL_DIR/bin/acmetool"
install -m 0644 "$ROOT/packaging/default.json" \
"$INSTALL_DIR/config/default.json"
xattr -cr "$STAGE"
find "$STAGE" -type d -exec chmod 0755 {} +
find "$STAGE" -type f ! -path '*/bin/acmetool' -exec chmod 0644 {} +
Danach wird ein Manifest erzeugt. Es erleichtert sowohl die Codeprüfung als auch den Vergleich mit den tatsächlichen Eingaben am Ende des Builds:
find "$STAGE" -print0 |
sort -z |
xargs -0 stat -f '%Sp %Su:%Sg %N' > "$PKGDIR/payload-manifest.txt"
grep -Eq '/(\.git|DerivedData|node_modules)(/|$)' \
"$PKGDIR/payload-manifest.txt" && exit 1 || true
Hier werden zuerst die erweiterten Attribute entfernt und anschließend die Berechtigungen gesetzt. So gelangen weder Herkunftsmarkierungen heruntergeladener Dateien noch ACLs des Entwicklungsrechners in das Artefakt. Die Konfigurationsvorlage darf nur Standardwerte enthalten; Laufzeittoken müssen erst bei der Bereitstellung injiziert werden.
Komponentenpaket mit pkgbuild erzeugen
Installationsskripte werden in einem separaten Verzeichnis abgelegt und müssen ausführbar sein. Sie sollten idempotent, schnell und nicht interaktiv arbeiten. Sie dürfen weder Eingaben vom Terminal lesen noch voraussetzen, dass ein bestimmter Benutzer angemeldet ist.
mkdir -p packaging/scripts
cat > packaging/scripts/postinstall <<'SH'
#!/bin/sh
set -eu
TARGET="/Library/Application Support/AcmeTool"
test -x "$TARGET/bin/acmetool"
chown -R root:wheel "$TARGET"
chmod 0755 "$TARGET" "$TARGET/bin" "$TARGET/bin/acmetool"
chmod 0644 "$TARGET/config/default.json"
exit 0
SH
chmod 0755 packaging/scripts/postinstall
pkgbuild \
--root "$STAGE" \
--identifier "com.example.acmetool.pkg" \
--version "1.4.0" \
--install-location "/" \
--scripts packaging/scripts \
"$PKGDIR/AcmeTool-component.pkg"
Bei nur einer Komponente ist das Komponentenpaket bereits installierbar. Wenn eine einheitliche Willkommensseite, ein Lizenztext oder die Kombination mehrerer Komponenten benötigt wird, lässt sich mit productbuild zusätzlich ein Distributionspaket erstellen:
productbuild \
--package "$PKGDIR/AcmeTool-component.pkg" \
"$PKGDIR/AcmeTool-1.4.0.pkg"
Falls eine Signatur erforderlich ist, wird die Installer-Signaturidentität in einer geschützten CI-Umgebung eingelesen und das endgültige Paket mit --sign "$INSTALLER_IDENTITY" gebaut. Nicht signierte Artefakte können weiterhin für vorgelagerte Tests verwendet werden, dürfen jedoch nicht als endgültiges Auslieferungsartefakt ausgegeben werden.
Statische Prüfung vor der Installation
Die erste Prüfschicht verändert das System nicht. Zunächst werden Paketkennung, Version und Payload kontrolliert. Danach wird das Distributionspaket entpackt, um Skripte und Metadaten zu prüfen:
PKG="$PKGDIR/AcmeTool-1.4.0.pkg"
EXPANDED="$PKGDIR/expanded"
pkgutil --check-signature "$PKG" || true
pkgutil --payload-files "$PKGDIR/AcmeTool-component.pkg"
rm -rf "$EXPANDED"
pkgutil --expand-full "$PKG" "$EXPANDED"
grep -R "com.example.acmetool.pkg" "$EXPANDED"
grep -R "1.4.0" "$EXPANDED"
find "$EXPANDED" -type f -name postinstall -exec sh -n {} \;
Wenn das endgültige Paket signiert sein muss, darf pkgutil --check-signature nicht fehlertolerant ausgeführt werden. Ein Rückgabewert ungleich null muss die Pipeline unmittelbar abbrechen. Absolute Build-Pfade, Fragmente privater Schlüssel und Benutzernamen aus dem Workspace sind ebenfalls zurückzuweisen:
if grep -R -E "$HOME|BEGIN (RSA |EC )?PRIVATE KEY" "$EXPANDED"; then
echo "sensitive build data found" >&2
exit 1
fi
Ein häufiger Fehler besteht darin, lediglich die Größe der Paketdatei zu vergleichen. Eine konstante Dateigröße bedeutet nicht, dass auch der Inhalt unverändert ist. Manifest, Kennung, Version, Skriptsyntax und Signaturstatus müssen jeweils separat geprüft werden.
Installation isoliert testen und Beleg prüfen
Die abschließende Abnahme muss in einer rücksetzbaren Testumgebung erfolgen, da installer sowohl in Systempfade als auch in die Installationsbelege schreibt. Der Testknoten sollte vor jedem Job auf einen bekannten Zustand zurückgesetzt und nicht gemeinsam mit alltäglichen Entwicklungssitzungen verwendet werden.
sudo installer -pkg "$PKG" -target /
test -x "/Library/Application Support/AcmeTool/bin/acmetool"
test "$(stat -f '%Sp' \
'/Library/Application Support/AcmeTool/bin/acmetool')" = "-rwxr-xr-x"
pkgutil --pkg-info "com.example.acmetool.pkg"
pkgutil --files "com.example.acmetool.pkg" |
sort > "$PKGDIR/installed-files.txt"
"/Library/Application Support/AcmeTool/bin/acmetool" --version
Eine erfolgreiche Installation besagt lediglich, dass das Skript den Rückgabewert null geliefert hat. Zusätzlich ist zu prüfen, ob die ausführbare Datei startet, die Konfigurationsvorlage geparst werden kann und die Version im Installationsbeleg stimmt. Installationsprotokoll und Dateiliste sollten als CI-Artefakte gespeichert werden. Bei einem Fehler ist zunächst der entsprechende Zeitraum aus /var/log/install.log zu sichern. Erst danach wird die Testumgebung zurückgesetzt, statt den Job sofort erneut auszuführen und damit den Fehlerzustand zu überschreiben.
Die Abschlussprüfung lässt sich auf fünf feste Punkte reduzieren: Das Staging-Verzeichnis enthält keine zusätzlichen Dateien, Berechtigungen und Eigentümer entsprechen dem Vertrag, die Paketversion steigt monoton, die Signatur des endgültigen Artefakts ist gültig und nach der isolierten Installation lassen sich sowohl der Befehl als auch der Installationsbeleg verifizieren. Damit wird das PKG von einem „doppelklickbaren Archiv“ zu einem prüfbaren, reproduzierbaren und regressionstestbaren technischen Auslieferungsartefakt.
Häufig gestellte Fragen
Sollte pkgbuild direkt das Repository als Root verwenden?
Nein. Erstellen Sie ein separates Staging-Verzeichnis, das ausschließlich die zu installierenden Dateien enthält. So gelangen Quelltexte, Caches, Protokolle und lokale Metadaten nicht in das Paket.
Lässt sich die PKG-Pipeline ohne Installer-Signaturzertifikat testen?
Ja. Prüfen Sie zunächst ein unsigniertes Paket auf Struktur, Rechte, Skripte und isolierte Installation. Die Release-Datei wird danach signiert und mit pkgutil --check-signature kontrolliert.
Konfigurieren Sie einen Cloud-Mac für Ihren nächsten Build- oder Inferenz-Job
Wählen Sie aus drei Apple-Silicon-Konfigurationen, sechs Knoten und festen Laufzeiten. Die tatsächliche Verfügbarkeit wird in Echtzeit in der Konsole angezeigt.