콘텐츠로 이동

워크북을 git으로 관리하기

워크북의 내용을 로컬 리포지토리에 파일로 꺼내어, git으로 diff·리뷰·PR에 올리고, 그대로 다시 반영할 수 있습니다. 채팅의 에이전트가 작성한 transform도 같은 위치에 나오므로, “AI가 작성한 SQL을 리뷰한 뒤 확정한다”는 운영이 그대로 성립합니다.

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
디렉터리내용pullpush
transforms/SQL / Python transform(agg/monthly.sql처럼 계층화 가능)✅✅ 변경분만 POST /transforms로 재실행(데이터 조작으로 계상). 의존 순서(업스트림부터)
sheets/표시용 시트 {"blocks": [...]}✅✅ PUT /sheets/{name}
charts/차트의 config + recipe(생성에 사용한 툴과 파라미터)✅❌ 읽기 전용 — 차트는 recipe에서 재생성하는 것으로, config를 손으로 고쳐도 재생성할 수 없습니다. 이력·확인용
data/--data로 opt-in한 base 테이블의 CSV(__d2b_row_id 포함)✅ export = branch✅ 서버 쪽에서 행 ID를 키로 하는 셀 단위 3-way merge. 양쪽에서 바뀐 셀은 workbook의 conflict 큐로(D2B의 값이 남음)

--data 는 base 테이블(행 자체가 진실의 원천인 표)에만 적용됩니다. mode=auto 의 구조화 출력은 derived(변환의 출력)라 분기할 수 없습니다 — 표의 행을 git 으로 왕복시키려면 --mode staged 로 넣어 parse spec 확인 후 materialize(= base 화)하거나, 행 API 로 생성하세요.

d2b.json이 매니페스트입니다. transform의 엔트리는 {name, artifact_name, args, layer, hash}(새 transform을 추가할 때는 파일과 이 엔트리를 작성합니다). hash는 마지막 동기화 시점의 digest(= 머지 베이스)로 CLI가 유지합니다 — push 후의 d2b.json은 commit해 주십시오.

새 엔트리를 손으로 쓸 때는 hash 를 쓰지 마세요(생략 — null 도 동일): 첫 push 가 값을 부여해 되씁니다.

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

워크스페이스 단위로 동기화하기

섹션 제목: “워크스페이스 단위로 동기화하기”

수백에서 수천 개의 워크북은 저장소 하나 = 워크스페이스 하나로 다룹니다. 루트의 d2b.json(원장)이 워크스페이스를 고정하고, 각 워크북은 workbooks/<제목>--<id 앞 8자리>/에 위의 레이아웃 그대로, 각자의 d2b.json과 함께 들어갑니다.

Terminal window
d2b pull --workspace WS # 처음: 원장을 만들고 워크스페이스의 모든 워크북을 가져옵니다(병렬, --jobs N)
d2b pull # 이후: 원장에서 읽습니다. 새 워크북은 저절로 나타납니다
d2b pull --prune # 워크스페이스를 떠난 워크북의 디렉터리를 삭제합니다(삭제가 diff에 보입니다)
d2b status --strict # 원장·디렉터리·서버가 어긋나면 exit 2(CI 필수 검사로)
d2b push --commit "$(git rev-parse --short HEAD)" # 변경된 워크북만 보냅니다
  • 소속은 서버의 사실입니다. pull은 워크스페이스의 모든 워크북을 나열하고, 원장에 없는 디렉터리는 절대 쓰지 않습니다. 다른 워크스페이스를 같은 저장소로 pull할 수 없으며, 이를 무시하는 플래그도 없습니다.
  • 워크북의 디렉터리는 첫 pull에서 고정되며 제목을 바꿔도 옮겨지지 않습니다. id는 디렉터리 이름과 각 transform 파일의 첫 줄 -- d2b ws=… wb=… transform=…에 있습니다(이 줄은 서버로 보내지지 않고 변경으로도 세지 않습니다).
  • push는 원장에 없는 디렉터리, 다른 워크북을 가리키는 헤더의 파일, 원장과 다른 workbook_id의 매니페스트를 발견하면 아무것도 보내지 않고 거부합니다.
  • CI에는 워크스페이스에 고정한 PAT를 주세요(POST /api/control/account/keys의 workspace_id, 또는 POST /api/v1/me/tokens의 resource: "workspace:<id>"). 설정이 잘못되어도 다른 워크스페이스는 서버가 403으로 막습니다. d2b login의 자격 증명은 계정 전체에 닿으므로 CI에는 맞지 않습니다.
  • data/(행 데이터)는 종전대로 워크북별 opt-in입니다(workbooks/<dir>/ 안에서 d2b pull --data TABLE).

원장이 없는 디렉터리는 위의 단일 워크북 동작을 그대로 유지합니다.

  • 텍스트 계열(transforms / sheets / charts)은 조용히 덮어쓰지 않습니다(행 API의 낙관적 잠금과 같은 계약): 마지막 동기화 이후 양쪽이 바뀌었으면 pull / push 모두 거부하고, 대상 파일을 JSON으로 반환합니다. --force 의 방향은 명령마다 다릅니다: push --force 는 로컬 쪽으로 덮어쓰기, pull --force 는 서버 쪽을 채택(로컬 편집 폐기)
  • data/는 다릅니다: 동시 편집은 서버의 3-way merge가 셀 단위로 판정하므로 거부하지 않습니다. push 후에는 머지 결과로 CSV를 다시 받아 새 branch를 만듭니다(D2B 쪽 변경도 로컬로 내려옴). 파생 테이블은 branch할 수 없습니다(400). 머지 상한 100,000행 — 마스터나 대응표 같은 작은 표 용도
  • 로컬에서 지운 파일은 서버를 지우지 않습니다(deleted_locally에 보고만). --prune으로 transform의 출력 테이블과 시트를 삭제(출력 테이블의 삭제는 workbooks:delete). data/는 추적만 해제할 뿐, 테이블은 지우지 않습니다
  • d2b.json 의 키는 해당 섹션 디렉터리 안의 정규화된 상대 경로만 허용합니다. 절대 경로, .., Windows 드라이브, 정규화되지 않은 경로는 pull / push 모두에서 거부합니다. 동기화 대상 파일(transforms/*.sql|py 등)이 심볼릭 링크인 경우도 거부합니다 — 링크를 통해서는 읽지도 쓰지도 않습니다(섹션이 읽지 않는 파일을 가리키는 링크는 그냥 무시합니다)

GitHub App도 D2B 쪽 연동 설정도 필요 없이, CLI만으로 리포지토리와 워크북의 왕복이 닫힙니다.

Terminal window
d2b github-workflow > .github/workflows/d2b.yml
# Secrets: D2B_API_KEY (a workbooks:write PAT). Variables: D2B_BASE_URL
  • main으로의 merge(동기화 대상 파일에 변경 있음) → d2b push --commit <sha> → 갱신된 d2b.json을 자동 commit
  • 정기 실행(기본 1시간마다)과 수동 → d2b pull → diff가 있으면 PR(d2b/pull 브랜치). 에이전트가 워크북 쪽에서 바꾼 것을 사람이 리뷰하고 merge하는 흐름

SDK에서는 client.transforms.list(wb) / client.sheets.list(wb) / client.charts.list(wb) / client.export.branch(wb, [table]) / client.tables.merge(...)가 같은 표면입니다.