Pular para o conteúdo

Gerenciar workbooks com git

Você pode extrair o conteúdo de um workbook como arquivos no seu repositório, passar tudo por diff, review e PR no git, e escrever de volta. Os transforms escritos pelo agente do chat aparecem no mesmo lugar, então a prática de “revisar o SQL escrito pela IA antes de confirmar” funciona sem adaptação.

Terminal window
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
DiretórioConteúdopullpush
transforms/Transforms SQL / Python (hierarquia permitida, como agg/monthly.sql)✅✅ Só o que mudou é re-executado via POST /transforms (contabilizado como operação de dados). Em ordem de dependência (do upstream para baixo)
sheets/Sheets de exibição {"blocks": [...]}✅✅ PUT /sheets/{name}
charts/Config + recipe dos charts (a tool e os parâmetros usados na geração)✅❌ Somente leitura — um chart é regenerado a partir do recipe; corrigir o config à mão não permite regenerar. Para histórico e conferência
data/CSV das tabelas base com opt-in via --data (com __d2b_row_id)✅ export = branch✅ 3-way merge célula a célula no servidor, chaveado por row ID. Células alteradas dos dois lados vão para a fila de conflicts do workbook (o valor do D2B permanece)

--data só se aplica a tabelas base (cujas linhas são a própria fonte de verdade). A saída estruturada do mode=auto é derivada (saída de um transform) e não pode ser ramificada — para levar as linhas de uma tabela pelo git, ingira com --mode staged, revise o parse spec e materialize (isso produz uma tabela base), ou crie-a via API de linhas.

O d2b.json é o manifesto. A entrada de um transform é {name, artifact_name, args, layer, hash} (ao adicionar um transform novo, escreva o arquivo e essa entrada). O hash é o digest do último sync (= a base do merge) e a CLI o mantém — faça commit do d2b.json depois do push.

Ao escrever uma entrada nova à mão, omita hash (omitir — ou null — dá no mesmo): o primeiro push o atribui e grava de volta.

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

Centenas ou milhares de pastas de trabalho são tratadas como um repositório = um workspace. O d2b.json da raiz (o livro-razão) fixa o workspace, e cada pasta de trabalho fica em workbooks/<título>--<8 primeiros caracteres do id>/ com o layout acima, incluindo o próprio d2b.json.

Terminal window
d2b pull --workspace WS # primeira vez: grava o livro-razão e traz todas as pastas de trabalho do workspace (em paralelo, --jobs N)
d2b pull # depois: a partir do livro-razão; uma pasta nova aparece sozinha
d2b pull --prune # remove os diretórios das pastas que saíram do workspace (a remoção aparece no diff)
d2b status --strict # exit 2 quando livro-razão, diretórios e servidor divergem (torne-o verificação obrigatória no CI)
d2b push --commit "$(git rev-parse --short HEAD)" # só as pastas de trabalho alteradas são enviadas
  • O pertencimento é um fato do servidor: pull lista todas as pastas de trabalho do workspace e nunca grava um diretório que o livro-razão não conheça. Outro workspace não pode ser trazido para o mesmo repositório — não há opção para forçar.
  • O diretório de uma pasta de trabalho é fixado no primeiro pull; mudar o título não o move. O id está no nome do diretório e na primeira linha de cada arquivo de transformação, -- d2b ws=… wb=… transform=… (a linha nunca é enviada ao servidor nem conta como alteração).
  • push não envia nada quando encontra um diretório desconhecido do livro-razão, um arquivo cujo cabeçalho nomeia outra pasta de trabalho ou um manifesto vinculado a outro workbook_id.
  • Dê ao CI um PAT fixado ao workspace (workspace_id em POST /api/control/account/keys, ou resource: "workspace:<id>" em POST /api/v1/me/tokens): mesmo mal configurado, o servidor responde 403 para qualquer outro workspace. A credencial de d2b login alcança a conta inteira e não é a chave certa para o CI.
  • data/ (linhas) continua sendo opt-in por pasta de trabalho (d2b pull --data TABLE dentro de workbooks/<dir>/).

Um diretório sem livro-razão mantém o comportamento de pasta única descrito acima.

  • Arquivos de texto (transforms / sheets / charts) nunca são sobrescritos em silêncio (o mesmo contrato do lock otimista da API de linhas): se os dois lados mudaram desde o último sync, tanto pull quanto push são recusados e os arquivos afetados são retornados em JSON. A direção de --force depende do comando: push --force impõe o seu lado local; pull --force adota o lado do servidor (descartando suas edições locais)
  • data/ é diferente: edições simultâneas não são recusadas, porque o 3-way merge do servidor arbitra célula a célula. Depois do push, o CSV é baixado de novo com o resultado do merge e um novo branch é aberto (as mudanças do lado do D2B também descem para o local). Tabelas derivadas não aceitam branch (400). Limite de merge: 100,000 linhas — para tabelas pequenas, como masters e tabelas de correspondência
  • Arquivos apagados localmente não apagam nada no servidor (só entram no relatório em deleted_locally). Com --prune, as tabelas de saída dos transforms e as sheets são excluídas (excluir tabelas de saída exige workbooks:delete). Em data/, só o rastreamento é removido — a tabela não é apagada
  • As chaves de d2b.json devem ser caminhos relativos normalizados dentro do próprio diretório da seção: caminhos absolutos, .., unidades do Windows e caminhos não normalizados são recusados tanto no pull quanto no push. Um arquivo sincronizado que seja um link simbólico (transforms/*.sql|py e afins) também é recusado — nada é lido nem escrito através de um link (um link para um arquivo que a seção nunca lê é simplesmente ignorado)

Sem GitHub App e sem configuração de integração no lado do D2B: só com a CLI, a ida e volta entre o repositório e o workbook se fecha.

Terminal window
d2b github-workflow > .github/workflows/d2b.yml
# Secrets: D2B_API_KEY (a workbooks:write PAT). Variables: D2B_BASE_URL
  • Merge na main (com mudanças nos arquivos sincronizados) → d2b push --commit <sha> → commit automático do d2b.json atualizado
  • Agendado (padrão: a cada 1 hora) e manual → d2b pull → se houver diff, abre um PR (branch d2b/pull). É o fluxo em que uma pessoa revisa e faz merge do que o agente mudou no lado do workbook

No SDK, a mesma superfície é client.transforms.list(wb) / client.sheets.list(wb) / client.charts.list(wb) / client.export.branch(wb, [table]) / client.tables.merge(...).