Zum Inhalt springen

Workbooks mit git verwalten

Der Inhalt eines Workbooks lässt sich als Dateien in das eigene Repository ziehen, dort mit git durch Diff, Review und Pull Request führen und direkt zurückschreiben. Auch Transforms, die der Chat-Agent geschrieben hat, erscheinen am selben Ort — die Arbeitsweise „von der KI geschriebenes SQL erst reviewen, dann festschreiben“ ergibt sich damit von selbst.

Terminal-Fenster
d2b pull --workbook WB # → transforms/ sheets/ charts/ + d2b.json
d2b pull --data customers # also track a small base table as data/customers.csv (export = branch)
git add -A && git commit -m "pull from D2B"
# ... edit transforms/*.sql|py, sheets/*.json, data/*.csv
d2b push --dry-run # what would be sent (only files that changed)
d2b push --commit "$(git rev-parse --short HEAD)" # apply the changes → pin the git sha as a named version
git commit -am "d2b push" # push updates d2b.json (sync hashes) — commit it too
VerzeichnisInhaltpullpush
transforms/SQL- / Python-Transforms (hierarchisch möglich, z. B. agg/monthly.sql)✅✅ Nur geänderte werden per POST /transforms erneut ausgeführt (zählt als Datenoperation). In Abhängigkeitsreihenfolge (upstream zuerst)
sheets/Darstellungs-Sheets {"blocks": [...]}✅✅ PUT /sheets/{name}
charts/Chart-config + recipe (das Tool und die Parameter der Erzeugung)✅❌ Read-only — Charts werden aus dem recipe neu erzeugt; eine von Hand geänderte config lässt sich nicht neu erzeugen. Für Historie und Sichtprüfung
data/CSVs der per --data opt-in aufgenommenen base-Tabellen (mit __d2b_row_id)✅ export = branch✅ Serverseitiger 3-way merge auf Zellebene mit Zeilen-ID als Schlüssel. Auf beiden Seiten geänderte Zellen landen in der Conflict-Queue des Workbooks (der D2B-Wert bleibt stehen)

--data gilt nur für Basistabellen (deren Zeilen ihre eigene Wahrheitsquelle sind). Die strukturierte Ausgabe von mode=auto ist abgeleitet (Transform-Output) und kann nicht gebrancht werden — um Zeilen einer Tabelle über git zu führen, per --mode staged ingestieren, Parse-Spec prüfen und materialisieren (ergibt eine Basistabelle), oder sie über die Zeilen-API anlegen.

d2b.json ist das Manifest. Ein Transform-Eintrag ist {name, artifact_name, args, layer, hash} (für einen neuen Transform die Datei plus diesen Eintrag anlegen). hash ist der Digest zum Zeitpunkt der letzten Synchronisation (= Merge-Basis) und wird von der CLI gepflegt — d2b.json nach dem push committen.

Beim handschriftlichen Anlegen eines neuen Eintrags hash weglassen (weglassen — oder null — ist gleichwertig): der erste Push vergibt ihn und schreibt ihn zurück.

{
"workbook_id": "…",
"transforms": {
"agg/monthly.sql": {
"name": "agg/monthly",
"artifact_name": "product_sales", "args": {"src": "sales"}, "layer": null,
"hash": "…"
}
},
"sheets": {"summary.json": {"name": "summary", "hash": "…"}},
"charts": {"trend.json": {"name": "trend", "readonly": true, "hash": "…"}},
"data": {"customers.csv": {"table": "customers", "branch_id": "…", "hash": "…"}}
}

Hunderte oder Tausende Workbooks werden als ein Repository = ein Workspace behandelt. Die d2b.json im Wurzelverzeichnis (das Verzeichnisbuch) legt den Workspace fest, und jedes Workbook landet in workbooks/<Titel>--<erste 8 Zeichen seiner id>/ mit dem Layout von oben, eigene d2b.json inklusive.

Terminal-Fenster
d2b pull --workspace WS # beim ersten Mal: schreibt das Verzeichnisbuch und holt jedes Workbook des Workspace (parallel, --jobs N)
d2b pull # danach: aus dem Verzeichnisbuch; ein neues Workbook erscheint von selbst
d2b pull --prune # entfernt die Verzeichnisse von Workbooks, die den Workspace verlassen haben (sichtbar im Diff)
d2b status --strict # exit 2, wenn Verzeichnisbuch, Verzeichnisse und Server nicht übereinstimmen (als Pflichtprüfung in CI)
d2b push --commit "$(git rev-parse --short HEAD)" # nur geänderte Workbooks werden gesendet
  • Die Zugehörigkeit ist eine Tatsache des Servers: pull listet jedes Workbook des Workspace auf und schreibt nie ein Verzeichnis, das das Verzeichnisbuch nicht kennt. Ein anderer Workspace lässt sich nicht in dasselbe Repository holen – es gibt keinen Schalter dafür.
  • Das Verzeichnis eines Workbooks wird beim ersten Pull festgelegt; eine Umbenennung verschiebt es nicht. Seine id steht im Verzeichnisnamen und in der ersten Zeile jeder Transformationsdatei, -- d2b ws=… wb=… transform=… (diese Zeile geht nie an den Server und zählt nie als Änderung).
  • push sendet gar nichts, wenn es ein dem Verzeichnisbuch unbekanntes Verzeichnis, eine Datei mit dem Header eines anderen Workbooks oder ein an eine andere workbook_id gebundenes Manifest findet.
  • Geben Sie CI ein an den Workspace gepinntes PAT (workspace_id bei POST /api/control/account/keys oder resource: "workspace:<id>" bei POST /api/v1/me/tokens): Selbst bei falscher Konfiguration antwortet der Server für jeden anderen Workspace mit 403. Die Anmeldung aus d2b login reicht über das ganze Konto und ist nicht der richtige Schlüssel für CI.
  • data/ (Zeilen) bleibt ein Opt-in je Workbook (d2b pull --data TABLE innerhalb von workbooks/<dir>/).

Ein Verzeichnis ohne Verzeichnisbuch behält das oben beschriebene Verhalten für ein einzelnes Workbook.

  • Textartefakte (transforms / sheets / charts) werden nie stillschweigend überschrieben (derselbe Vertrag wie das optimistische Locking der Zeilen-API): Haben sich seit der letzten Synchronisation beide Seiten geändert, lehnen pull wie push ab und geben die betroffenen Dateien als JSON zurück. Die Richtung von --force hängt vom Befehl ab: push --force setzt die lokale Seite durch; pull --force übernimmt die Server-Seite (lokale Änderungen gehen verloren)
  • data/ verhält sich anders: Gleichzeitige Änderungen werden nicht abgelehnt, der serverseitige 3-way merge entscheidet auf Zellebene. Nach dem push wird das CSV aus dem Merge-Ergebnis neu geholt und ein neuer branch eröffnet (auch Änderungen der D2B-Seite landen so lokal). Abgeleitete Tabellen lassen sich nicht branchen (400). Merge-Limit 100.000 Zeilen — gedacht für kleine Tabellen wie Stammdaten oder Zuordnungstabellen
  • Lokal gelöschte Dateien löschen nichts auf dem Server (nur ein Bericht unter deleted_locally). --prune löscht Output-Tabellen von Transforms und Sheets (das Löschen von Output-Tabellen erfordert workbooks:delete). Bei data/ endet nur das Tracking, die Tabelle bleibt bestehen
  • Schlüssel in d2b.json müssen normalisierte relative Pfade innerhalb ihres eigenen Section-Verzeichnisses sein: absolute Pfade, .., Windows-Laufwerke und nicht normalisierte Pfade werden bei pull wie push abgelehnt. Eine synchronisierte Datei, die ein symbolischer Link ist (transforms/*.sql|py und dergleichen), wird ebenfalls abgelehnt — über einen Link wird nichts gelesen oder geschrieben (ein Link auf eine Datei, die die Section nie liest, wird einfach ignoriert)

Weder eine GitHub App noch eine Integrationskonfiguration auf D2B-Seite ist nötig — der Roundtrip zwischen Repository und Workbook schließt sich allein über die CLI.

Terminal-Fenster
d2b github-workflow > .github/workflows/d2b.yml
# Secrets: D2B_API_KEY (a workbooks:write PAT). Variables: D2B_BASE_URL
  • Merge nach main (mit Änderungen an synchronisierten Dateien) → d2b push --commit <sha> → das aktualisierte d2b.json wird automatisch committet
  • Zeitgesteuert (Standard: stündlich) und manuell → d2b pull → bei Differenzen ein Pull Request (Branch d2b/pull). So reviewen Menschen, was der Agent auf Workbook-Seite geändert hat, und mergen es

Im SDK sind client.transforms.list(wb) / client.sheets.list(wb) / client.charts.list(wb) / client.export.branch(wb, [table]) / client.tables.merge(...) dieselbe Oberfläche.