워크북을 git으로 관리하기
워크북의 내용을 로컬 리포지토리에 파일로 꺼내어, git으로 diff·리뷰·PR에 올리고, 그대로 다시 반영할 수 있습니다. 채팅의 에이전트가 작성한 transform도 같은 위치에 나오므로, “AI가 작성한 SQL을 리뷰한 뒤 확정한다”는 운영이 그대로 성립합니다.
d2b pull --workbook WB # → transforms/ sheets/ charts/ + d2b.jsond2b 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/*.csvd2b 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 versiongit commit -am "d2b push" # push updates d2b.json (sync hashes) — commit it too레이아웃
섹션 제목: “레이아웃”| 디렉터리 | 내용 | pull | push |
|---|---|---|---|
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과 함께 들어갑니다.
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 Actions로 왕복 자동화하기
섹션 제목: “GitHub Actions로 왕복 자동화하기”GitHub App도 D2B 쪽 연동 설정도 필요 없이, CLI만으로 리포지토리와 워크북의 왕복이 닫힙니다.
d2b github-workflow > .github/workflows/d2b.yml# Secrets: D2B_API_KEY (a workbooks:write PAT). Variables: D2B_BASE_URLmain으로의 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(...)가 같은 표면입니다.