Zum Hauptinhalt springen

bea 0.2.0: eine Installation, die gesamte Beancount-Toolchain

Veröffentlicht 17 Minuten LesezeitMike ThriftMike Thrift
bea 0.2.0: eine Installation, die gesamte Beancount-Toolchain
Auf dieser Seite

Wenn Sie jemals einem Kollegen, einem neuen Laptop oder einem nächtlichen Cron-Job eine funktionierende Beancount-Einrichtung übergeben haben, wissen Sie, dass die Buchhaltung nie der schwierige Teil war. Der schwierige Teil war die Toolchain: ein passendes Python, bean-check und bean-query im Pfad, eine Berichtsbibliothek, die für eine einzige Bilanz herangezogen wurde, und ein Formatierer, der Ihre Dateien in dem Moment umschreibt, in dem Sie ihm eine Frage stellen. bea 0.2.0, veröffentlicht am 12. September 2026, ersetzt diese Checkliste durch eine einzige Installation. Der Befehl bea trägt jetzt die vollständige native Beancount-Toolchain, führt sie in einer verwalteten Engine aus, die er selbst bereitstellt, und behält den maschinenlesbaren Vertrag bei, auf den Skripte und KI-Agenten bereits angewiesen sind.

Dies ist die Versionshinweise für 0.2.0, geschrieben so, wie wir eine Veröffentlichung intern verfolgen: was ausgeliefert wurde, was sich darunter geändert hat, wie es verifiziert wurde, bevor es einen Paketindex erreichte, was es bewusst noch nicht tut und wie man aktualisiert. Wenn Sie stattdessen die Geschichte der ersten Verwendung möchten, sind der 0.1.0-Startbeitrag und die CLI-Schnellstart die kürzeren Lektüren.

Die Veröffentlichung auf einen Blick

Zwei Kanäle veröffentlichen denselben Befehl. Wählen Sie einen und bestätigen Sie dann, dass er mit seiner Version antwortet:

$ brew install bex-co/tap/bea        # macOS und Linuxbrew
$ uv tool install beancount-io       # überall mit uv und Python 3.12 oder neuer
$ bea --version
bea 0.2.0
bea 0.2.0
cli-v0.2.02026-09-12
Engine
beancount 3.2.3 beanquery 0.2.0
optional
beangulp 0.2.0 beanprice 2.1.0
python
3.12 3.14

Die 0.2.0-Release-Karte: das Tag und das Veröffentlichungsdatum, die von der verwalteten Engine festgelegten Beancount- und Beanquery-Versionen, die beiden optionalen Engine-Funktionen und die Python-Versionen, auf denen die Veröffentlichung installiert und getestet wurde.

FeldWert
Version0.2.0, Tag cli-v0.2.0, veröffentlicht auf PyPI und im bex-co/homebrew-tap Homebrew-Tap am 2026-09-12
Vorherige Veröffentlichung0.1.0, getaggt am 2026-09-09, drei Tage zuvor
Änderungssatz27 Commits mit Bezug auf die CLI, 119 geänderte Dateien, etwa 12.300 hinzugefügte und 2.100 entfernte Zeilen
Engine-FestlegungenBeancount 3.2.3 und Beanquery 0.2.0 in der Basis-Engine; Beangulp 0.2.0 und Beanprice 2.1.0 als optionale Funktionen
SchlagzeileJedes native Beancount-Tool unter einem Präfix, bereitgestellt von einer verwalteten Engine; der JSON-Envelope und der Exit-Code-Vertrag aus 0.1.0 sind unverändert

Was sich darunter geändert hat: die verwaltete Engine

In 0.1.0 importierte bea Beancount in seinen eigenen Prozess, wie es jedes Python-Tool tun würde. Das funktionierte, aber es machte den Abhängigkeitsgraphen der CLI zum Abhängigkeitsgraphen von Beancount, und es ließ „Zuerst Beancount installieren“ als ungeschriebenen Schritt in jeder Anleitung.

0.2.0 zieht eine Linie durch die Mitte des Programms. Das bea-Frontend, der Teil, der die Befehle, die Optionen und die Darstellung besitzt, lädt Beancount, Beanquery oder den eingebetteten Fava-Berichtscode nie. Lokale Ledger-Arbeit läuft in einer verwalteten Engine: einer separaten Python-Umgebung, die bea aus einem hash-pinned Lock bereitstellt und als untergeordneten Interpreter startet. Das Frontend sendet eine JSON-Anfrage über diese Grenze und rendert, was zurückkommt. Sie installieren Beancount nicht, setzen bean-*-Tools nicht in Ihren Pfad und denken nicht darüber nach, welches Python sie gefunden haben.

Wie die Engine ankommt, hängt vom Kanal ab:

  • Homebrew erstellt die Frontend- und Engine-Umgebungen während der Installation. Lokale Befehle verwenden die keg-lokale Engine ohne weiteren Download.
  • PyPI (uv tool install oder pipx) stellt bei der ersten Verwendung bereit. Der erste lokale Befehl, der die Engine benötigt, lädt die festgelegte Kombination herunter, was Netzwerkzugriff und uv im Pfad einmal erfordert. Spätere Befehle verwenden sie offline von ~/.local/share/bea/engine/<version> oder unter XDG_DATA_HOME, wenn Sie diese gesetzt haben.

Drei Eigenschaften folgen aus diesem Design, und jede entfernt ein Support-Ticket, das wir bereits gesehen haben:

  1. Upgrades bleiben gepaart. bea upgrade übergibt das Update an den Paketmanager, der diese Kopie installiert hat, und baut dann die passende Engine neu auf, sodass Frontend und Engine nie auf verschiedene Versionen driften können.
  2. Eine defekte Engine heilt sich selbst. Wenn eine Bereitstellung auf halbem Wege fehlschlägt, wird die verwaltete Umgebung verworfen und beim nächsten erfolgreichen Versuch neu aufgebaut. Verirrte bean-check-Binärdateien anderswo im Pfad werden ignoriert, nicht versehentlich aufgegriffen.
  3. Schwere optionale Teile bleiben optional. Das Beangulp-Import-Framework benötigt die Systembibliothek libmagic, und Beanprice zieht Abhängigkeiten für den Kursabruf nach sich. Beides ist nicht in der Basis-Engine. Sie aktivieren sie explizit, nur in der Engine.
$ bea engine status
$ bea engine enable beangulp     # Import-Helfer; benötigt die Systembibliothek libmagic
$ bea engine enable beanprice    # bean-price Kursabruf

bea engine status meldet, ob die Engine bereitgestellt ist und welche optionalen Funktionen aktiviert sind, und benötigt dafür kein Netzwerk. Wenn eine Bereitstellung bei der ersten Verwendung fehlschlägt, beheben Sie das Netzwerk oder uv und führen Sie einen beliebigen lokalen Befehl wie bea check erneut aus. Installieren Sie nicht pip install beancount daneben: Das Frontend wird es nicht verwenden.

Jedes native Tool, ein Präfix

Die Engine ist der Mechanismus. Die benutzerseitige Änderung ist Parität: Jedes ausführbare Programm, das das Upstream-Beancount-Projekt ausliefert, hat jetzt ein bea-Pendant, mit denselben weitergeleiteten Argumenten und derselben erhaltenen Ausgabe.

$ bea check                                    # bean-check, plus beas --json-Envelope
$ bea format main.bean -o clean.bean           # bean-format: stdout standardmäßig, -i schreibt neu
$ bea query "SELECT account, sum(position) GROUP BY account"
$ bea doctor context main.bean 2026-01-02      # alle elf bean-doctor-Operationen
$ bea example --seed 1 -o example.beancount    # bean-example
$ bea treeify < balances.txt                   # treeify
$ bea ingest identify --config ingest.py inbox # Beangulp, nach engine enable
$ bea price -e USD:yahoo/AAPL                  # bean-price, nach engine enable
bean-check
bea check
bean-format
bea format
bean-query
bea query
bean-doctor
bea doctor
bean-example
bea example
treeify
bea treeify
beangulp
bea ingest bea engine enable beangulp
bean-price
bea price bea engine enable beanprice

Die Paritätskarte: Die sechs nativen Beancount-Programme über der gestrichelten Linie funktionieren sofort; die beiden darunter werden an Beangulp und Beanprice weitergeleitet, sobald Sie diese Funktion in der Engine aktivieren.

Einige davon verdienen mehr als eine Zeile in einer Tabelle.

bea check ist bean-check mit beas JSON-Envelope obenauf: dieselbe Validierung, dieselben Fehlermeldungen und unter --json dieselben Felder valid und errors, die Skripte bereits parsen.

bea format hat sein Verhalten geändert, und es ist die eine Änderung in dieser Veröffentlichung, die ein Skript überraschen kann. In 0.1.0 schrieb bea format PATH die Datei neu. Jetzt gibt es den formatierten Text auf stdout aus und lässt die Datei unberührt. --in-place (-i) ist das, was neu schreibt, --output FILE (-o) schreibt woanders hin, --check ist das CI-Gate, das mit 1 endet, wenn Dateien formatiert werden müssen, und --dry-run listet auf, was sich ändern würde. Dies folgt bean-format, dessen Standard der sichere ist: Ein Befehl, der einen Pfad liest und ihn stillschweigend neu schreibt, kann nicht zuerst ausprobiert werden. Formatierung ist eine Texttransformation, kein Parse, daher verweigert sie nicht mehr eine Datei mit einem Syntaxfehler; sie richtet aus, was sie erkennt, und lässt den Rest. Führen Sie bea check für die Gültigkeit aus.

bea query ist um die gesamte native Oberfläche gewachsen. Es akzeptiert BQL als Argument, von stdin oder in der interaktiven Shell, die jetzt die Upstream-Beanquery-Shell ist, die als untergeordneter Prozess mit ihren .format, .output, .run- und .set-Befehlen intakt gestartet wird. --format wählt text, csv oder beancount-Darstellung, --numberify teilt Beträge in eine Spalte pro Währung auf, -o schreibt in eine Datei und --source URI reicht eine native Beanquery-Quelle direkt durch.

bea doctor legt alle elf bean-doctor-Operationen offen: lex, parse, roundtrip, directories, list-options, print-options, context, linked, region, missing-open und display-context. Wenn Sie jemals ein Buchungsproblem mit bean-doctor context debuggt haben, ist es dasselbe Werkzeug an derselben Adresse.

bea example und bea treeify sind der native Generator und der native Baum-Renderer, unverändert weitergeleitet.

bea ingest und bea price leiten an Beangulps identify, extract und archive bzw. an bean-price weiter, nach bea engine enable. Der Python-freie CSV-Pfad, bea import --csv, benötigt keines von beiden und ist unverändert.

Eine Regel verbindet die weitergeleiteten Befehle: doctor, example, treeify, price und ingest geben ihre Argumente unverändert an Upstream weiter und behalten die Ausgabe und den Exit-Status von Upstream bei. Das bedeutet auch, dass sie das Ledger als eigenes Positionsargument nehmen, wie in bea doctor lex main.bean, nicht über globales --file. Der Envelope und die Exit-Code-Kategorien unten beschreiben beas eigene Befehle.

Der Vertrag, dem Skripte weiterhin vertrauen können

Nichts an der maschinenlesbaren Oberfläche hat sich bewegt. Globales --json legt weiterhin einen Envelope auf stdout mit bea, target, data und truncated, plus limit auf begrenzten Listen und page auf paginierten gehosteten Listen. Beträge sind Dezimalzeichenfolgen, nie Floats, und Daten sind ISO YYYY-MM-DD. --json impliziert --no-input; das gilt auch für ein Nicht-Terminal-stdin oder eine wahre CI-Variable, sodass ein unbeaufsichtigter Job nie auf einen Menschen wartet. --strict verweigert Teilantworten auch in einem Terminal, und --allow-errors jedes Lesebefehls optet wieder ein.

Ein Fehlschlag schreibt nichts auf stdout und genau ein Objekt auf stderr:

{
  "error": {
    "category": "validation",
    "message": "Ledger hat 3 Fehler. Übergeben Sie --allow-errors, um trotzdem zu berichten.",
    "exit_code": 1,
    "details": ["main.bean:1: Transaktion ist nicht ausgeglichen: (2.50 USD)"]
  }
}
0
ok
1
validation
2
usage
3
auth
4
conflict

Die fünf Exit-Codes und die category-Zeichenfolge, die jeder im JSON-Fehlerobjekt trägt. Ein Skript verzweigt auf die Zahl; ein Mensch liest die Kategorie.

CodeKategorieBedeutung
0keineErfolg, einschließlich Vorschauen und absichtlicher Duplikat-Überspringungen
1validationLedger- oder Validierungsfehler und der Sammelbegriff für jeden anderen Laufzeitfehler
2usageSchlechte Argumente, ein fehlendes Ziel oder Extra, oder Eingabe unter --no-input erforderlich
3authAuthentifizierungs- oder Berechtigungsfehler, einschließlich eines schreibgeschützten Ziels
4conflictEine gleichzeitige Änderung, ein Import, der eine Duplikat-Überprüfung benötigt, oder ein Schreiben, dessen Ergebnis unbekannt ist

Zwei Details sind für jeden wichtig, der bei einem Fehlschlag erneut versucht. Ein Nicht-Null-Exit bedeutet nicht universell, dass nichts geändert wurde: add transactions --partial kann die akzeptierten Zeilen schreiben, format -i über mehrere Dateien kann einige neu schreiben, bevor es bei einer fehlschlägt, und cloud ledger create --clone kann das Ledger erstellen, bevor der Klon fehlschlägt. Lesen Sie error.result, bevor Sie eine Mutation erneut versuchen. Und gehostete Befehle bilden den HTTP-Status des Servers auf dieselbe Tabelle ab, wobei sie die eigene Nachricht des Servers behalten: 401 und 403 enden mit 3, 400 mit 2, 409 mit 4, und alles andere, einschließlich Ratenbegrenzung, endet mit 1. Ein Schreiben, dessen Ergebnis die CLI nicht kennen kann, wie ein Timeout mitten im Löschen, endet mit 4 und sagt dies, anstatt zu raten.

Der Automatisierungsleitfaden führt eine jq-Pipeline Ende zu Ende durch diesen Envelope.

Behobene Probleme, die mitfuhren

Eine Paritätsveröffentlichung ist auch eine Gelegenheit, die Defekte zu schließen, die eine erste Veröffentlichung aufdeckt. Diese landeten zwischen den beiden Tags, jeweils mit einem Regressionstest:

  • Zahlen werden als Festkommatext geschrieben, nie in wissenschaftlicher Notation, einschließlich der Eröffnungssalden, die bea init rendert. Ein Ledger, das 1E+3 sagt, ist technisch gültig und praktisch unlesbar.
  • Kostenlose Lose überleben die JSON-Serialisierung mit ihren Daten und Beschriftungen intakt, und Lose-Beschriftungen werden korrekt maskiert, wenn eine Transaktion geschrieben wird.
  • Explizite Null-Posten sind während des Imports echte Beträge, anstatt als „weggelassen, bitte gleiche mich aus“ gelesen zu werden.
  • CSV-Importe laufen durch einen strengen Leser. Die Header-Erkennung entfernte früher Spaltennamen, während die Extraktion die Rohschlüssel behielt, sodass ein aufgefüllter Header, den die Dokumente zu akzeptieren versprachen, als fehlende Spalte fehlschlug. Jetzt werden Namen einmal entfernt, eine zugeordnete Spalte muss genau einmal erscheinen, und ein ungeschlossenes Anführungszeichen schlägt mit seiner Zeilennummer fehl, bevor etwas geschrieben wird.
  • BQL lädt den exakten Ledger-Pfad statt einer URL-geparsten Verbindungszeichenfolge, sodass ungewöhnliche Pfade so aufgelöst werden, wie der Rest der CLI sie auflöst.
  • bea balance <term> summiert nur, was es zeigt. Ein beibehaltenes Elternteil meldet nicht mehr die Summen ausgeschlossener Geschwister, ein nicht zusammenhängender, nicht bewerteter Bestand lässt eine USD-Auswahl nicht mehr fehlschlagen, und der Envelope meldet den angewendeten Filter. Ein fehlerhaftes --account-Muster bei Berichten endet mit 2 als der Nutzungsfehler, der es ist.
  • Stderr im JSON-Modus ist immer ein Objekt, auch wenn tolerierte Warnungen vor dem Fehlschlag kommen.
  • Gehostete Anmeldeinformationen schlagen früh und konsistent fehl: Ein BEA_TOKEN mit Leerzeichen wird vor jeder Anfrage abgelehnt, eine widerrufene Anmeldeinformation wird von cloud status und von Ledger-Befehlen gleich gemeldet, und owner/name wird vor einer Bestätigungsaufforderung oder einem authentifizierten Aufruf validiert. cloud logout lässt BEA_TOKEN unberührt, und cloud ledger list --json gibt die Seite zurück, die es tatsächlich bedient hat.
  • Die Homebrew-Formel legt die genaue PyPI-Artefakt-URL fest, sodass eine Tap-Installation und eine PyPI-Installation nachweislich dieselben Bytes sind.

Wie es verifiziert wurde, bevor Sie es sahen

Eine Veröffentlichung ist eine Behauptung, und die Pipeline ist der Beweis. Ein cli-v0.2.0-Tag muss einen Commit auf main benennen, dessen pyproject.toml-Version genau übereinstimmt; der Workflow weigert sich bei allem anderen, einschließlich Prerelease-Suffixen. Von dort:

  1. Die vollständige Prüfungssuite läuft zuerst. make check-all deckt Linting, Formatierung, striktes mypy, Dead-Code-Erkennung, den Drift-Check der generierten Referenz und die Testsuite ab. Der Release-Pull-Request verzeichnet 635 bestandene Tests.
  2. Der Engine-Lock wird exportiert und hash-pinned, und die Quelldistribution und das Wheel werden einmal gebaut. Jeder spätere Schritt testet genau diese Artefakte, nicht einen Neubau.
  3. Saubere Installationen auf drei Betriebssystemen und zwei Pythons. Das Wheel wird durch uv tool und die sdist durch pip auf Linux, macOS und Windows installiert, auf Python 3.12 und 3.14, einschließlich des optionalen KI-Extras. Ein Homebrew-Job installiert die sdist durch ein temporäres Tap auf macOS und Linux.
  4. Die Veröffentlichung ist sequentiell und tokenlos. PyPI erhält die Artefakte durch vertrauenswürdige Veröffentlichung, sodass kein langlebiges API-Token existiert, das lecken könnte; die GitHub-Release wird mit angehängten Veröffentlichungsattestaten erstellt; und Formula/bea.rb wird mit der sdist-URL und dem Hash, den PyPI tatsächlich ausgeliefert hat, in das öffentliche Tap gepusht.
  5. Smoke-Tests nach der Veröffentlichung installieren aus den echten Indizes. Separate Jobs installieren die festgelegte Version von PyPI und vom öffentlichen Tap und führen dieselben Kundensmoke-Tests gegen das installierte Programm aus. Ein Fehlschlag dort rollt nichts zurück, bedeutet aber, dass die Veröffentlichung Aufmerksamkeit benötigt, bevor jemand davon erfährt.

Dieser Beitrag wird jenseits von Schritt fünf geschrieben.

Aktualisieren von 0.1.0

Führen Sie das Upgrade über den Manager aus, der Ihre Kopie installiert hat, oder lassen Sie es bea tun:

$ bea upgrade --check      # installierte und neueste Versionen und den Befehl melden, der ausgeführt würde
$ bea upgrade              # brew upgrade bea, uv tool upgrade beancount-io, oder pipx upgrade beancount-io

Nachdem der Manager fertig ist, aktualisiert bea upgrade die verwaltete Engine, sodass die beiden gepaart bleiben. Dann prüfen Sie drei Dinge:

  • Jedes Skript, das bea format PATH ausgeführt hat, um eine Datei neu zu schreiben, benötigt jetzt bea format -i PATH. Der alte Standard konnte nicht in der Vorschau angesehen werden, der neue kann es.
  • Jedes Skript, das sich darauf verlassen hat, dass format einen Syntaxfehler abfängt, sollte dafür bea check aufrufen, da die Formatierung nicht mehr parst.
  • PyPI-Installationen benötigen einmal Netzwerk und uv für den ersten lokalen Befehl nach dem Upgrade, damit die Engine bereitgestellt werden kann. Homebrew-Installationen benötigen nichts.

Alles, was Ihre Skripte bereits parsen, die Envelope-Schlüssel, die Dezimalzeichenfolgen und die Exit-Codes, ist unverändert. Das Feld bea im Envelope liest jetzt 0.2.0.

Was diese Veröffentlichung nicht tut

  • Gehostete Zielsteuerung ist nicht implementiert. Es gibt kein --ledger-Flag; lokale Befehle lesen lokale Dateien und laden nie implizit eine hoch. Gehostete Ledger werden unter bea cloud verwaltet und als Git-Klone bearbeitet.
  • bea ask benötigt weiterhin das ask-Extra und Beancount.io-Anmeldeinformationen, und es unterstützt kein --json. Die Standardinstallation trägt keine KI-Abhängigkeiten.
  • Beangulp und Beanprice sind optional, und Beangulp benötigt die Systembibliothek libmagic. bea import --csv deckt Bankexporte ohne beide ab.
  • Weitergeleitete native Befehle geben keinen Envelope aus. Wenn Sie strukturierte Ausgabe von einer Doctor-Operation benötigen, ist das eine Anfrage, die wir gerne hören würden.

Seit dem Tag hat main bereits die erste Runde QA auf 0.2.0 aufgenommen, und sie wird mit der nächsten Veröffentlichung fahren: bea format liest stdin als Filter, und sein -o FILE-Modus antwortet mit einem Envelope, der benennt, was es geschrieben hat; --json check weigert sich bei bean-check-nur-Flags, und --json wird auf doctor, example und treeify direkt verweigert, damit ein Skript nativen Text nicht für einen Envelope hält; --json query -o FILE schreibt den Envelope atomar in die Datei, mit --numberify auch auf JSON angewendet; bea engine status benennt, welche Engine-Stufe bedient; eine BQL-Abfrage, die mit einem Kommentar beginnt, läuft; natives Pass-through --help funktioniert, bevor die Engine bereitgestellt ist; und die .output der Abfrage-Shell stellt den ursprünglichen Stream nach einer fehlgeschlagenen Umleitung wieder her.

Wohin als Nächstes

Behalten Sie Ihre Bücher als Code

Eine Toolchain, die Sie in einer Zeile installieren können, ist eine Toolchain, die Sie jedem geben können: einem Mitgründer, einem Buchhalter, einem CI-Läufer, einem KI-Agenten. Beancount.io bietet Klartext-Buchhaltung, die transparent, versioniert und reproduzierbar bleibt, mit bea als dem Befehl, der ein lokales Ledger ehrlich hält, und dem gehosteten Dienst als dem Ort, an dem Ihr Team, Ihr Telefon und Ihr Assistent dieselben Bücher treffen. Installieren Sie bea und führen Sie Ihre erste Prüfung durch, und wenn die Veröffentlichung etwas tut, das Sie nicht erwartet haben, ist das GitHub-Repository der Ort, an dem wir es hören möchten.

Diesen Artikel teilen

Quelle: https://beancount.io/de/blog/2026/09/16/bea-0-2-0-one-install-whole-beancount-toolchain

Veröffentlicht: 16. September 2026