Zum Inhalt springen
TAA

Case Study

AutoBrew v2.4: Snapshot-Engine, CLI und Ärger beim Signing

AutoBrew v2.4 ist erschienen: automatische Snapshots vor jedem Upgrade, ein schlankes CLI über ein URL-Scheme und ein Diff-Viewer. Dazu ein offener Bericht über Apples Signing-Modell.

25. Mai 2026, Marcel R. G. Berger, 7 Min. Lesezeit

  • macos app entwickeln
  • autobrew
  • swift
  • homebrew
  • build-in-public
  • macos
Teilen

Zu Beginn der Woche stand AutoBrew auf Version 2.3.0, und ich hatte eine klare Liste mit acht Funktionen, die unbedingt in 2.4 sollten. Am Ende der Woche war v2.4.0 live. Dazwischen lag ein Streit mit Xcode und GitHub Actions über das Signing, mit dem ich so nicht gerechnet hatte. Hier steht ohne Beschönigung, was diese Woche in meiner macOS-App-Entwicklung passiert ist.

AutoBrew v2.4: Was sich für Nutzer ändert

Automatische Snapshots vor jedem Upgrade

Mit dieser Funktion macht die App einen echten Sprung. Bisher hat AutoBrew Homebrew-Casks automatisch aktualisiert, aber wenn ein Update kaputt war, gab es keinen einfachen Weg zurück. V2.4 löst das: Bevor AutoBrew ein Cask automatisch aktualisiert, legt die App jedes Mal einen Snapshot der App-Daten des Nutzers an. Geht das Update schief, genügt ein Klick in der neuen Update-Verlaufsansicht, um den vorherigen Stand wiederherzustellen.

Technisch läuft das über die bestehende AppSnapshotEngine, die ich jetzt tief in den Upgrade-Ablauf eingehängt habe. Der Snapshot entsteht vor dem eigentlichen brew upgrade-Aufruf, synchron und bevor das Cask-Binary angerührt wird. Die Verlaufsansicht ist ein neuer Eintrag in der Seitenleiste von BrewStore. Sie listet die abgeschlossenen automatischen Upgrades, die neuesten zuerst, mit Status-Symbolen pro Cask, die ein eigener Parser aus der Ausgabe von brew upgrade gewinnt. Das klingt trivial, ist es aber nicht. Brew mischt in seiner Ausgabe Fortschrittszeilen, Warnungen und die eigentlichen Ergebnisse, und das Format weicht je nach Cask und Homebrew-Version leicht ab. Ich habe den Parser dreimal umgebaut, bis er zuverlässig zwischen “erfolgreich”, “fehlgeschlagen” und “unklar” unterscheiden konnte.

Selektives Wiederherstellen und der Snapshot-Diff-Viewer

Was nützt ein Snapshot, wenn man beim Wiederherstellen nur alles oder nichts zurückbekommt? Ich wollte feiner unterscheiden können. V2.4 erlaubt es, einzelne Bestandteile eines Snapshots gezielt zurückzuspielen: Preferences getrennt von Caches, Caches getrennt von den Daten in Application Support. Der Nutzer sieht, was in welchem Bestandteil steckt, und entscheidet selbst.

Dazu kommt der Snapshot-Diff-Viewer, der zwei Snapshots direkt vergleicht. Auf einen Blick ist zu sehen, welche Dateien sich zwischen dem Stand vor und nach dem Upgrade verändert haben. Das hilft besonders, wenn man verstehen will, warum sich eine App nach dem Update anders verhält, ohne blind wiederherstellen zu müssen. Umgesetzt habe ich das mit FileManager-Enumerationen beider Snapshot-Verzeichnisse, deren Pfade und Prüfsummen ich vergleiche. Angezeigt wird das Ergebnis in einer einfachen SwiftUI-Liste mit farblich markierten Zeilen. Technisch nichts Spektakuläres, der Nutzen ist trotzdem real.

Das schlanke CLI über ein URL-Scheme

Diese Funktion hatte ich schon länger im Kopf: ein Kommandozeilenwerkzeug, das AutoBrew-Aktionen auslöst, ohne selbst ein vollständiges macOS-App-Bundle mit eigenem Signing-Profil zu sein. Die Lösung ist einfach und elegant zugleich. Das CLI ist ein schlankes Binary, das Befehle als URL-Scheme-Aufrufe an die laufende AutoBrew-App weiterreicht. Aus autobrew upgrade firefox wird intern der Aufruf autobrew://upgrade?cask=firefox. Die App empfängt ihn und führt die Aktion aus, während das CLI auf die Antwort wartet. Das Binary braucht kein eigenes Provisioning-Profil, weil es kein eigenständiges App-Bundle ist, sondern ein Werkzeug. Apples Signing-Modell unterscheidet an dieser Stelle, und genau hier bin ich diese Woche an eine harte Grenze gestoßen. Dazu gleich mehr.

Changelog-Vorschau bei den ausstehenden Freigaben

AutoBrew hat einen Freigabemodus: Upgrades werden nicht automatisch installiert, sondern dem Nutzer zur Genehmigung vorgelegt. Bisher sah man dabei nur den Cask-Namen und die neue Versionsnummer, und das reicht nicht. V2.4 holt die Release Notes von GitHub direkt in die Ansicht der ausstehenden Freigaben. Man sieht also, was sich in einem Update geändert hat, bevor man auf “Installieren” klickt. Technisch ist das ein einfacher Aufruf der GitHub-API gegen api.github.com/repos/{owner}/{repo}/releases/tags/{version}, zwischengespeichert in BrewStore, damit nicht jeder Aufruf der Ansicht eine neue Anfrage auslöst.

Das gefällt mir. Es ist die erste Funktion in AutoBrew, die Daten von außerhalb des lokalen Systems holt, und das fühlt sich richtig an, weil sie einen echten Engpass beseitigt: Man wollte wissen, was ein Update bringt, bevor man es freigibt.

Verwaiste Pakete aufräumen und brew doctor

Dazu kommen zwei kleinere, aber nützliche Ergänzungen. brew autoremove lässt sich jetzt direkt aus BrewStore heraus aufrufen. Homebrew hinterlässt nach Upgrades und Deinstallationen regelmäßig verwaiste Abhängigkeiten, und statt im Terminal von Hand aufzuräumen, gibt es dafür jetzt einen Button. Außerdem wird die Ausgabe von brew doctor geparst und als eigener Abschnitt zum Zustand des Systems angezeigt: Warnungen und Probleme sind strukturiert lesbar statt als rohe Terminalausgabe. Beides klingt nach Kleinkram. Wer AutoBrew als wichtigste Oberfläche für Homebrew nutzt, bekommt damit aber das Gefühl von Vollständigkeit, das vorher noch gefehlt hat.

Externer Snapshot-Speicher über Security-Scoped Bookmarks

Wer Snapshots nicht im Standardverzeichnis für App-Daten haben will, etwa weil dort der Platz knapp ist oder weil sie auf eine externe SSD sollen, kann jetzt einen eigenen Speicherort wählen. Die App merkt sich den Zugriff über ein Security-Scoped Bookmark, das macOS genau für diesen Zweck bereitstellt. Der Nutzer wählt einmal einen Ordner im Öffnen-Dialog, die App speichert das Bookmark in UserDefaults, und ab dann landet jeder neue Snapshot dort. Es gibt keinen erneuten Dialog und keinen Verlust der Berechtigung nach einem Neustart. Konzeptionell war das einfach, in der Praxis hat es aber zwei Anläufe gebraucht. Beim ersten habe ich das Bookmark zu früh im Lebenszyklus der App aufgelöst. Nach einem Neustart war der Scope nicht mehr aktiv, und der Schreibvorgang schlug fehl, ohne dass es jemand merkte. Stille Fehler sind die schlimmsten.

Der Kampf mit dem Signing: Warum ich heute dreimal denselben CI-Job gemergt habe

Apples Code-Signing-Modell ist und bleibt lästig. Diese Woche hat es mich einen halben Tag gekostet.

Das Problem: Ich wollte die CI-Pipeline auf eine wiederverwendbare Workflow-Bibliothek für GitHub Actions umstellen, marcelrgberger/ci-open-source@v1, die ich für meine öffentlichen Projekte pflege. Der erste Versuch lief mit automatischem Signing. Das ist Xcode Cloud vorbehalten und funktioniert auf normalen GitHub-Runnern nicht zuverlässig. Der zweite Versuch nutzte mein wiederverwendbares Workflow-Setup mit ci-open-source, scheiterte aber daran, dass das CLI-Hilfsbinary einen Provisioning-Profile-Specifier in den Build-Einstellungen hatte. Für Kommandozeilenwerkzeuge ist das schlicht verboten: Sie sind kein App-Bundle, und Apple erlaubt dort keine Profile.

Am Ende bin ich zum manuellen Signing mit ausdrücklich angegebenem Profilbezeichner zurückgekehrt. Den Profile-Specifier habe ich aus dem CLI-Target entfernt und das überflüssige Secret keychain-password gestrichen, weil match-password das seit Version 1.3.0 abdeckt. Drei Merges, ein Revert, ein halber Tag. Das Ergebnis ist jetzt sauberer als vor dem Durcheinander, aber ich wäre lieber direkt dort angekommen.

Was ich daraus mitnehme: Hat eine macOS-App ein Kommandozeilenwerkzeug als eigenes Target, muss man das Signing beider Targets von Anfang an getrennt planen. Das App-Bundle braucht ein Provisioning-Profil, das CLI-Werkzeug nicht. Xcode trifft diese Unterscheidung nicht von selbst. Setzt man sie nicht ausdrücklich, kracht es irgendwann in der CI, meistens im ungünstigsten Moment.

Wie es weitergeht

Die Retry-Logik für fehlgeschlagene Upgrades ist bereits enthalten: Automatische Wiederholungsversuche nach einer Stunde, nach vier Stunden und nach zwölf Stunden, bevor ein Upgrade als endgültig gescheitert gilt. Das ist eine Kleinigkeit, die im Alltag viel ausmacht. Ein Update scheitert oft, weil das Netzwerk kurz weg war oder ein Prozess blockiert hat, und nicht, weil das Paket kaputt ist.

Auch Cask-Sammlungen sind in v2.4 enthalten: Casks lassen sich zu benannten Gruppen zusammenfassen und mit einem Klick als Gruppe installieren oder deinstallieren. Daraus soll eine Umzugsfunktion werden. Mac neu aufsetzen, Sammlung importieren, fertig.


Wenn Sie überlegen, eine native macOS-App für Ihr Unternehmen entwickeln zu lassen, ob als internes Werkzeug, zur Automatisierung oder als eigenes Produkt, zeigt AutoBrew gut, wie ich arbeite: Funktion für Funktion, öffentlich und mit offener Dokumentation der Rückschläge. Was ich für AutoBrew baue, baue ich mit demselben Anspruch für Kunden. Sehen Sie sich meine Leistungen an oder schreiben Sie mir direkt.

AutoBrew v2.4.0 steht auf GitHub bereit: DMG herunterladen, in den Ordner Programme ziehen, fertig.

Teilen