Ir al contenido

Gestionar workbooks con git

Puedes extraer el contenido de un workbook como archivos en tu repositorio local, pasarlo por diff, revisión y PR en git, y escribirlo de vuelta tal cual. Los transforms que escribió el agente del chat aparecen en el mismo lugar, así que el flujo “revisar el SQL que escribió la IA antes de confirmarlo” funciona sin más.

Ventana de terminal
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
DirectorioContenidopullpush
transforms/Transforms SQL / Python (admiten jerarquía, como agg/monthly.sql)✅✅ Solo lo cambiado se re-ejecuta vía POST /transforms (se contabiliza como operación de datos). En orden de dependencias (desde aguas arriba)
sheets/Sheets de presentación {"blocks": [...]}✅✅ PUT /sheets/{name}
charts/Config + recipe del chart (la herramienta y los parámetros con que se generó)✅❌ Solo lectura — un chart se regenera desde su recipe, y corregir el config a mano no permite regenerarlo. Para historial y verificación
data/CSV de las tablas base con opt-in vía --data (con __d2b_row_id)✅ export = branch✅ 3-way merge a nivel de celda en el servidor, con el ID de fila como clave. Las celdas cambiadas en ambos lados van a la cola de conflicts del workbook (prevalece el valor de D2B)

--data solo aplica a tablas base (cuyas filas son su propia fuente de verdad). La salida estructurada de mode=auto es derivada (salida de un transform) y no puede ramificarse — para llevar las filas de una tabla por git, ingiérela con --mode staged, revisa el parse spec y materializa (eso produce una tabla base), o créala vía la API de filas.

d2b.json es el manifiesto. La entrada de un transform es {name, artifact_name, args, layer, hash} (al agregar un transform nuevo, escribe el archivo y esta entrada). hash es el digest del último punto de sincronización (= la base del merge) y lo mantiene la CLI — haz commit del d2b.json después de cada push.

Al escribir una entrada nueva a mano, omite hash (omitirlo — o null — es lo mismo): el primer push lo asigna y lo escribe de vuelta.

{
"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": "…"}}
}

Cientos o miles de libros se manejan como un repositorio = un espacio de trabajo. El d2b.json de la raíz (el libro mayor) fija el espacio de trabajo, y cada libro queda en workbooks/<título>--<8 primeros caracteres de su id>/ con el diseño de arriba, incluido su propio d2b.json.

Ventana de terminal
d2b pull --workspace WS # primera vez: escribe el libro mayor y trae todos los libros del espacio (en paralelo, --jobs N)
d2b pull # después: desde el libro mayor; un libro nuevo aparece solo
d2b pull --prune # elimina los directorios de libros que salieron del espacio (la eliminación se ve en el diff)
d2b status --strict # exit 2 cuando el libro mayor, los directorios y el servidor no coinciden (hazlo comprobación obligatoria en CI)
d2b push --commit "$(git rev-parse --short HEAD)" # solo se envían los libros que cambiaron
  • La pertenencia es un hecho del servidor: pull enumera todos los libros del espacio y nunca escribe un directorio que el libro mayor no conozca. Otro espacio de trabajo no puede traerse al mismo repositorio: no hay opción para forzarlo.
  • El directorio de un libro se fija en su primer pull; cambiar el título no lo mueve. Su id está en el nombre del directorio y en la primera línea de cada transformación, -- d2b ws=… wb=… transform=… (esa línea nunca se envía al servidor ni cuenta como cambio).
  • push no envía nada cuando encuentra un directorio que el libro mayor no conoce, un archivo cuyo encabezado nombra otro libro o un manifiesto ligado a otro workbook_id.
  • Dale a CI un PAT fijado al espacio de trabajo (workspace_id en POST /api/control/account/keys, o resource: "workspace:<id>" en POST /api/v1/me/tokens): aun con una configuración errónea, el servidor responde 403 para cualquier otro espacio. La credencial de d2b login alcanza toda la cuenta y no es la clave adecuada para CI.
  • data/ (filas) sigue siendo opt-in por libro (d2b pull --data TABLE dentro de workbooks/<dir>/).

Un directorio sin libro mayor conserva el comportamiento de un solo libro descrito arriba.

  • Los archivos de texto (transforms / sheets / charts) no se sobrescriben en silencio (el mismo contrato que el bloqueo optimista de la API de filas): si ambos lados cambiaron desde la última sincronización, tanto pull como push se rechazan y devuelven en JSON los archivos afectados. La dirección de --force depende del comando: push --force impone tu lado local; pull --force adopta el lado del servidor (descartando tus ediciones locales)
  • data/ es distinto: la edición simultánea no se rechaza, porque el 3-way merge del servidor arbitra celda por celda. Tras el push, el CSV se vuelve a descargar con el resultado del merge y se abre un branch nuevo (los cambios del lado de D2B también aterrizan en local). Las tablas derivadas no admiten branch (400). Límite del merge: 100,000 filas — pensado para tablas pequeñas, como maestros o tablas de equivalencias
  • Borrar un archivo en local no borra nada en el servidor (solo se reporta en deleted_locally). Con --prune se eliminan las tablas de salida de los transforms y los sheets (eliminar tablas de salida exige workbooks:delete). En data/ solo se deja de rastrear: la tabla no se borra
  • Las claves de d2b.json deben ser rutas relativas normalizadas dentro de su propio directorio de sección: las rutas absolutas, .., las unidades de Windows y las rutas sin normalizar se rechazan tanto en pull como en push. Un archivo sincronizado que sea un enlace simbólico (transforms/*.sql|py y similares) también se rechaza: nunca se lee ni se escribe a través de un enlace (un enlace a un archivo que la sección no lee se ignora sin más)

Automatizar la ida y vuelta con GitHub Actions

Sección titulada «Automatizar la ida y vuelta con GitHub Actions»

No hace falta una GitHub App ni ninguna configuración de integración del lado de D2B: la ida y vuelta entre el repositorio y el workbook se cierra solo con la CLI.

Ventana de terminal
d2b github-workflow > .github/workflows/d2b.yml
# Secrets: D2B_API_KEY (a workbooks:write PAT). Variables: D2B_BASE_URL
  • Merge a main (con cambios en archivos sincronizados) → d2b push --commit <sha> → commit automático del d2b.json actualizado
  • Programado (por defecto cada 1 hora) y manual → d2b pull → si hay diferencias, un PR (branch d2b/pull). El flujo donde una persona revisa lo que el agente cambió del lado del workbook y le hace merge

En el SDK, la misma superficie es client.transforms.list(wb) / client.sheets.list(wb) / client.charts.list(wb) / client.export.branch(wb, [table]) / client.tables.merge(...).