Skip to content

feat(cube-cli): add cube dbt generate for CI-side cube generation - #11864

Merged
MikeNitsenko merged 8 commits into
masterfrom
mikhail/cub-4489-cube-dbt-generate
Sep 15, 2026
Merged

MikeNitsenko merged 8 commits into
masterfrom
mikhail/cub-4489-cube-dbt-generate

Conversation

@MikeNitsenko

@MikeNitsenko MikeNitsenko commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Summary

The CLI half of CUB-4489. cube dbt generate converts a dbt manifest and writes the cube YAML to your working copy — committing nothing, creating no branch — so a pipeline commits the result through its own review, CODEOWNERS and branch protection. Cube needs no access to the dbt repository.

dbt parse
cube dbt generate DEPLOYMENT_ID --manifest target/manifest.json --out .

The server side (output: "files" plus GET /dbt-sync/{id}/generated-files) merged in cubedevinc/cubejs-enterprise#14922.

Decisions worth a look

  • --out is the project ROOT, not the cube folder. Generated paths are project-relative (model/cubes/dbt/…) and are written beneath it as they come, so the output path saved on the integration keeps deciding where cubes live. The alternative — stripping the prefix — would re-derive a server-side decision on the client.
  • Two guards run before the wait, so a server doing something other than what was asked is caught in seconds rather than after thirty minutes. A returned branch name means an older Cube ignored output and committed anyway; that's an error naming the branch, because by then it exists in the customer's repo and the review mail has gone out. This mirrors the existing ensure_manifest_source.
  • The file read polls the way the result read does. A run that just reported COMPLETED is still closing and can 404 for a moment — and it matters more here, because these files are held only briefly, so treating an early 404 as the answer would lose the entire output.
  • resolve_within refuses absolute paths and ... These paths come from the API, and this is the first place the CLI writes a remote-supplied path to disk, so it's load-bearing rather than defensive. Every path is resolved before any file is written, so a rejected one can't leave a half-written tree.

Reuse

The manifest read and its JSON-object check, wait_for_sync, and verify_manifest_source are all sync --manifest's, unchanged.

Docs

Both halves of CUB-4489's docs condition:

  • reference/cli.mdx — the generate command, --out semantics, --check, and what --check does not catch.
  • docs/integrations/dbt.mdx — the page taught only "Cube converts and commits to a review branch" and said nothing about the topology this feature exists for. It now frames the choice by the question that decides it (who commits the cubes) and carries a complete CI job that takes the manifest as an artifact, since that is the realistic shape: no dbt repository anywhere near the runner.

Test plan

  • cargo test dbt:: — 22 passing, including three new: the generate payload asks for files and never a branch; a returned branch name is refused with the branch named; hostile paths (../, model/../../, absolute, empty) are refused
  • cargo clippy --all-targets clean, cargo fmt --check clean
  • Manual run against staging-mikenitsenko-2 on the merged server build:
$ cube dbt generate 1 --manifest ~/cube-dbt-demo/target/manifest.json --out /tmp/cub4489-live
dbt generate 980d5897 started
dbt sync: WRITING_CUBE_FILES 78% — Writing generated cube files
✓ wrote 4 generated file(s) under /tmp/cub4489-live
  /tmp/cub4489-live/model/cubes/dbt/dbt_dim_customer_scd.yml
  …
Nothing was committed — review and commit these through your own workflow.

Verified on the live tenant: files land under --out at the server-supplied
paths with real content (1382 / 1553 / 1491 / 1255 bytes); --check passes
against what was just written; --check after tampering with one file and
deleting another reports differs / is missing and exits 1; --json on
that path emits outcome: "differing" with {path, status} entries; and the
sync history shows branchName empty for all five runs, so nothing was
committed and no branch was created.

One thing the run turned up that is NOT from this PR: while ai-engineer was
restarting, a plain committing sync returned 500 after launchDbtSync had
already created the branch row, leaving an orphaned dbt-sync/* branch with no
history row behind it. The server guards this ordering against a missing repo
config but not against an ai-engineer outage. Filing separately.

Note for reviewers: local stable here is 1.86 and a dependency now needs 1.88, so the above ran on +1.90.0.

@mintlify

mintlify Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated
cubed3 🟢 Ready View Preview Sep 14, 2026, 9:12 AM

💡 Tip: Enable Automations to automatically generate PRs for you.

@github-actions github-actions Bot added pr:community Contribution from Cube.js community members. docs Issues that require a documentation improvement rust Pull requests that update Rust code javascript Pull requests that update Javascript code and removed pr:community Contribution from Cube.js community members. labels Sep 14, 2026
@claude

claude Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Claude finished @MikeNitsenko's task in 2m 41s —— View job


Review: 1 blocking, 3 medium, 3 low — the CLI change itself is solid; the commit is carrying 230 unrelated build artifacts.

Full review
  • Gather context (diff, changed files)
  • Read rust/cube-cli/src/commands/dbt.rs in full
  • Check docs changes (docs-mintlify/reference/cli.mdx)
  • Check prior review threads (none exist — first round)
  • Post findings (5 inline comments)

🔴 Blocking — the commit contains 230 Next.js build artifacts

Of the 232 changed files, 230 are docs/.next/** — webpack chunks, *.pack.gz binaries, .woff2 fonts, pages/*.js bundles. That's 142,328 of the 142,662 added lines. The actual change is:

 docs-mintlify/reference/cli.mdx   |  40 +++++-
 rust/cube-cli/src/commands/dbt.rs | 295 +++++++++++++++++++++++++++++

docs/ doesn't exist in origin/master at all (git cat-file -t origin/master:docs"exists on disk, but not in origin/master"), so this isn't a stale-artifact refresh — the branch is newly committing the dev-server output of a docs site that was removed from the repo. Root .gitignore has no .next entry, which is why nothing caught it.

git rm -r --cached docs/.next
printf '.next\n' >> .gitignore   # or docs/.gitignore
git commit --amend

Fix this →

Adding a .next line to .gitignore is worth doing regardless — docs-mintlify is also a Node site and the next person will hit the same thing.


🟠 Medium

# Where Issue
1 dbt.rs:285–302 The "returned no files" bail is unreachable — the poll predicate already requires non-empty items, so a genuinely empty run spins the full 30s and fails with a generic .advising_nothing() timeout instead of the message written for it.
2 dbt.rs:294 output::field returns "" for an absent key, so an item missing content silently truncates a committed cube file to zero bytes and exits 0. The path side is protected by accident via resolve_within(""); content isn't.
3 dbt.rs:955–966 --check only walks server-returned files, so a cube left behind by a deleted dbt model passes the gate forever. cli.mdx claims the stronger property ("fail a PR whose committed cubes no longer match its dbt project").

🟡 Low

# Where Issue
4 dbt.rs:1008–1012 --json prints the start payload — no file list. The machine-readable path gives a pipeline strictly less than the human one.
5 dbt.rs:963 Err(_) => "{} is missing" labels a permissions error or directory-in-the-way as missing, sending the reader to a re-run that will also fail.
6 dbt.rs:229–236 Three doc comments run 7–9 lines where a sentence or two carries the risk (resolve_within, fetch_generated_files, ensure_nothing_was_committed). Consistent with the file's existing style, so take it or leave it.

What's good

The design decisions in the description hold up against the code:

  • resolve_within is the right shape. Rejecting is_absolute() and every non-Normal/CurDir component catches .., RootDir and Windows Prefix in one pass — and the "lexical only, symlinks deliberately out of scope" reasoning is correct for a check about what the server can name. Resolving all paths before writing any (collect::<Result<Vec<_>>>()) genuinely does prevent the half-written tree. The hostile-path test covers the four cases that matter.
  • Both guards before the wait is the right call, and ensure_nothing_was_committed picking branchName as the tell — free, synchronous, unambiguous — is neater than a capability probe. Naming the branch in the error is correct: at that point it exists in the customer's repo.
  • Reuse is real, not claimed: read_manifest / parse_manifest, wait_for_sync, verify_manifest_source, RESULT_FETCH_TIMEOUT, poll.min(RESULT_FETCH_POLL_MAX) are all the existing paths unchanged, and generate_body mirrors start_body's "build the payload in one place" discipline.
  • The --out decision is right. Honouring project-relative paths as they come keeps the "where do cubes live" decision server-side; stripping the prefix would fork it. The docs explain this in the one place a user will look.
  • cli.mdx is a good doc edit — the --out warning, the "no review notification fires" note (the thing that bites on every-PR runs), and the "needs a deployment but not your dbt repo" note all answer questions a reader would otherwise file an issue about.

Not verified: I didn't build or run cargo test dbt:: / clippy here (toolchain per your note is +1.90.0); findings 1–3 are read off the control flow, not from a failing test. The unchecked "manual run against the merged server build" box in the description is the one I'd most want ticked before merge — findings 1 and 2 are both server-response-shape issues that a real run would expose immediately.

• branch [`mikhail/cub-4489-cube-dbt-generate`](https://github.com/cube-js/cube/tree/mikhail/cub-4489-cube-dbt-generate)

Comment thread rust/cube-cli/src/commands/dbt.rs Outdated
Comment thread rust/cube-cli/src/commands/dbt.rs Outdated
Comment thread rust/cube-cli/src/commands/dbt.rs
Converts a dbt manifest and writes the cube YAML to your working copy
instead of committing it to a Cube branch, so a pipeline can commit the
result through its own review, CODEOWNERS and branch protection. Cube needs
no access to the dbt repository.

`--out` is the project ROOT rather than the cube folder: the generated paths
are project-relative and are honoured as they come, so the output path saved
on the integration keeps deciding where cubes live instead of the CLI
re-deriving it.

Three things this leans on rather than reinvents. The manifest read and its
JSON-object check are `sync --manifest`'s. The wait is `wait_for_sync`. And
the file read polls the way the result read does, because a run that has
just reported COMPLETED is still closing and can 404 for a moment — which
matters more here, since the files are held only briefly and an early 404
treated as the answer would lose the whole run.

Two guards run before the wait, so a server doing something other than what
was asked is caught in seconds rather than after thirty minutes. A branch
name coming back means an older Cube ignored `output` and committed; that is
an error naming the branch, because by then it exists in the customer's
repository and the review mail has gone out.

`resolve_within` refuses absolute paths and any `..` component. These paths
come from the API and this is the first place the CLI writes a
remote-supplied path to disk, so the check is load-bearing rather than
defensive; every path is resolved before any file is written, so a rejected
one cannot leave a half-written tree.
Comment thread rust/cube-cli/src/commands/dbt.rs
@MikeNitsenko
MikeNitsenko force-pushed the mikhail/cub-4489-cube-dbt-generate branch from 4b3dd38 to ada130f Compare September 14, 2026 07:07
Comment thread rust/cube-cli/src/commands/dbt.rs Outdated
@claude

claude Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Claude finished @MikeNitsenko's task in 1m 11s —— View job


Re-review: no code change since the last round — 5 open threads still apply (1 high, 2 medium, 2 low)

Details
  • Gather context (diff, changed files)
  • Read rust/cube-cli/src/commands/dbt.rs in full
  • Check docs changes
  • Check prior review threads
  • Post findings

What changed

The synchronize event did not bring new content: head is still ada130f, one commit, and both changed files carry the same blobs I reviewed at 07:06 (dbt.rs 5ecd9d7, cli.mdx 0c5507a). So this is a re-affirmation pass, not a new review — no new inline comments posted, since every issue I'd raise is already an open thread on the same line with the same fix direction.

Open threads, unchanged

Sev Where Issue
High dbt.rs:294 A missing content key writes a zero-byte file over the committed one and exits 0. output::field returns String::new() for an absent key, so there is no way to tell "empty file" from "field missing".
Medium dbt.rs:302 The no files bail! is unreachable — the poll predicate already requires non-empty items — and the case it describes surfaces as a 30s wait timeout with .advising_nothing(), i.e. no advice.
Medium dbt.rs:966 --check can't detect cubes left behind by deleted dbt models; the doc in cli.mdx claims the stronger guarantee. Plus Err(_) => "is missing" mislabels permission/IsADirectory errors.
Low dbt.rs:1012 --json prints the start payload on both exits — strictly less than the human path, which lists every written file. sync --wait's wait_json(...) is the precedent.
Low dbt.rs:236 Nine-line doc comment on a twenty-line resolve_within; the load-bearing part is the last two sentences. Same shape on fetch_generated_files and ensure_nothing_was_committed.

Re-affirmed all 5 prior threads; resolved none, since none is addressed in the current diff.

Re-checked this round, still fine

  • resolve_within is genuinely load-bearing and the hostile-path test covers the four shapes that matter (../, embedded .., absolute, empty). Resolving every path before the first std::fs::write is the right ordering.
  • Both guards (ensure_nothing_was_committed, verify_manifest_source) run before the 30-minute wait — worth the two extra requests for the failure mode they catch.
  • output::items degrades safely on an unexpected envelope: a non-list payload becomes a single item whose path is empty, which resolve_within rejects rather than writing. The error text ("empty file path") is misleading for that case, but it does not write.
  • Docs land in /docs-mintlify per CLAUDE.md, and the dbt row in the command table was updated alongside the new section.

Not run: cargo test dbt:: / clippy — the PR body reports both clean on +1.90.0, and the toolchain here is the same stable that can't build the dependency tree.

· branch [`mikhail/cub-4489-cube-dbt-generate`](https://github.com/cube-js/cube/tree/mikhail/cub-4489-cube-dbt-generate)

Four findings, all real.

The empty-files bail was dead code: the poll only finished on a non-empty
payload, so a run that legitimately produced none spun for the full 30s and
failed with a generic timeout — the opposite of the message written for it.
An answer now ends the poll and only absence is "not yet".

A missing `content` was written as a zero-byte file over the committed one,
exiting 0. `output::field` yields "" for an absent key, so the one failure
this command must not have was the default. Content is now read as a
required string and an absent one is a protocol error.

`--check` reported a permissions error or a directory in the way as
"missing", sending the reader to re-run the write that would fail the same
way; only NotFound means "not committed yet" now. Its one-directional limit
is real and out of scope here — removing a cube whose dbt model was deleted
needs to know which subtree is Cube's to own — so the docs claim is narrowed
to what it does rather than left overstated.

`--json` printed the start payload, which carries nothing about what was
written: strictly less than the human path, for the caller most likely to
parse it. It now carries the file list, the output dir and the outcome,
following `wait_json`'s precedent.

Also trims three doc comments to the sentences that carry the risk.
@claude

claude Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Claude finished @MikeNitsenko's task in 2m 8s —— View job


Re-review of d667f10: all 4 prior findings genuinely fixed — 1 medium, 2 low new

Full review

  • Gather context (new commit diff)
  • Read rust/cube-cli/src/commands/dbt.rs in full
  • Check docs changes (docs-mintlify/reference/cli.mdx)
  • Check prior review threads (all 5 already resolved by the author)
  • Post findings (3 inline comments)

Prior round, verified fixed

Prior finding Fix Verdict
no files bail unreachable; empty run surfaced as a 30s timeout Poll now ends on any answer (Some(payload) => Done), only None is "not yet" (dbt.rs:279–282) Fixed — bail at :289 is now reachable
Missing content wrote a zero-byte file over the committed one, exit 0 read_generated_files takes content via file.get("content").and_then(Value::as_str) and bail!s on absence (:350–369), with the path named in the message; test a_file_with_no_content_is_refused_rather_than_written_empty Fixed, and the as_str form also rejects a non-string content
Err(_) => "is missing" mislabelled permission / IsADirectory errors Only ErrorKind::NotFound is "missing"; other kinds propagate with could not read {path} context (:1027–1036) Fixed
--check's one-directional limit overstated in docs cli.mdx now says it does not detect a cube left behind by a deleted dbt model, "which neither --check nor the write path removes" Fixed the right way — it states the limit instead of claiming the property
--json printed the start payload generate_json composes syncJobId / outputDir / outcome / fileCount / files (+ differing), wired into all three success exits, with a test Fixed on the success paths — see finding 1 for the failure path

The --out semantics, resolve_within, both pre-wait guards and the resolve-everything-before-writing ordering are unchanged and still right. 3 new tests, 25 total in dbt::.

New this round

🟠 Medium — dbt.rs:1013–1015: --json still prints nothing on the FAILED exit. The precedent this commit invoked (wait_json) emits its document before returning the failure — sync does exactly that at :900–905, and again on the result-read error path. generate returns straight out, so a --json run of a failed generate leaves stdout empty and the syncJobId a pipeline needs to call dbt logs only in a human sentence on stderr. An outcome: "failed" through generate_json covers both exits.

🟡 Low — dbt.rs:327–337: differing is prose, in a different path space than files in the same document. files holds project-relative paths as the server named them; differing holds root-joined display paths with the reason glued on as an English suffix ("/abs/…/orders.yml differs"). It's the one field a gate branches on, so [{"path": …, "status": "differs"}] would let a consumer join the two arrays.

🟡 Low — dbt.rs:289–294: the now-reachable empty-files bail names the wrong cause. With an answer ending the poll, the common way to land there is a run that genuinely produced nothing; expiry now shows up as None and a timeout. "kept only briefly … collect them right after it completes" sends the reader to re-run faster, which does nothing for a project with no cubes to give.

Nit, not posted inline: the three trimmed doc comments came back as new ones — read_generated_files (:342–349, 6 lines) and fetch_generated_files (:253–262, now two paragraphs) both restate in prose what the test beneath them asserts. The load-bearing sentence in the first is one clause: output::field yields "", so a zero-byte write would clobber a committed file. Consistent with the file's prevailing style, so take it or leave it.

Not verified: cargo isn't runnable in this job (command not permitted here), so cargo test dbt:: / clippy / fmt are unrun on my side — findings are read off control flow. The description's unchecked "manual run against the merged server build" box is still the one I'd want ticked: finding 1 and the empty-answer poll change are both server-response-shape behaviour a real run settles immediately.

Resolved no threads — the author had already resolved all 5 from the prior rounds, and each is genuinely addressed in this commit.

· branch [`mikhail/cub-4489-cube-dbt-generate`](https://github.com/cube-js/cube/tree/mikhail/cub-4489-cube-dbt-generate)

Comment thread rust/cube-cli/src/commands/dbt.rs
Comment thread rust/cube-cli/src/commands/dbt.rs
Comment thread rust/cube-cli/src/commands/dbt.rs
Three small ones from review.

A --json run that failed wrote empty stdout: the syncJobId, which is what a
pipeline needs to go read the logs, only reached stderr in an English
sentence. Both failing exits now emit a document with outcome "failed", so
every exit of the command emits exactly one.

`differing` carried prose in a different path space than `files` in the same
document. It now carries {path, status} keyed the way `files` is, which also
made the code smaller: the check loop collects pairs and only the human
branch formats them.

The empty-files message named expiry, which is now the unlikely cause —
expiry arrives as a 404 and a poll timeout. It names the likely one first: a
manifest with no models, or pull options that filtered them all out.
@claude

claude Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Claude finished @MikeNitsenko's task in 1m 28s —— View job


Re-review of 3b23538: all 3 prior findings fixed — 1 low new. Ready to merge once the manual-run box is ticked.

Full review

  • Gather context (new commit diff)
  • Read rust/cube-cli/src/commands/dbt.rs in full
  • Check prior review threads (all 8 already resolved by the author)
  • Post findings (1 inline comment)

Prior round, verified fixed

Prior finding Fix Verdict
--json printed nothing on the FAILED exit failed_json() closure emitted on the FAILED branch (:1021–1027) and on the fetch_generated_files error, which is now a match rather than ? (:1029–1038) Fixed on both exits named
differing carried prose, in a different path space than files Vec<(String, &str)> of project-relative path + "differs" / "missing"; generate_json emits {"path": …, "status": …}, and only the human branch formats a line (:1048–1074) Fixed — and the check loop got smaller, which is the right sign. Test asserts the shape
Empty-files bail named expiry, the now-unlikely cause "generated no cubes. Most likely its manifest had no models to convert, or the deployment's pull options filtered them all out", expiry demoted to a parenthetical (:289–293) Fixed — names the cause that actually reaches it, keeps the other

Unchanged and still right: --out as project root, resolve_within's lexical-only check, both pre-wait guards, and resolving every path before the first write.

New this round

🟡 Low — dbt.rs:1017: the comment claims an invariant the code doesn't hold. "Every exit of this command emits exactly one --json document" — but wait_for_sync(...).await? (:1015), resolve_within in the check loop, and the write loop's create_dir_all / write failures all still write empty stdout, each of them after syncJobId is known. The wait timeout is the one worth weighing: it's the failure a pipeline hits alongside FAILED, and sync --wait has the identical gap — so matching it is defensible, but then the sentence should say "every exit below this point". The partial-write case is the closer call, since it's the analogue of sync's result-read path, which deliberately does emit.

Same hunk, nit: " {} {status}" now prints …/orders.yml missing where it read is missing. Grammar was the only thing the old format string bought and it's gone on that arm only.

Comments

The two new comments in this commit earn their place under the 3-line rule — failed_json's explains why a failing exit needs a document at all (the syncJobIddbt logs path), and differing's states the keying contract a consumer depends on. The first is the one whose claim needs narrowing, per the finding above.

Not verified: cargo isn't runnable in this job (command not permitted), so cargo test dbt:: / clippy / fmt are unrun on my side — the PR body reports 25 tests + 2 new assertions clean on +1.90.0. The description's unchecked "manual run against a deployment on the merged server build" is the last thing I'd want ticked: the --json document shape and the empty-answer poll rule are both settled in seconds by one real run.

Resolved no threads — the author had already resolved all 8 from the prior rounds, and each is genuinely addressed.

· branch [`mikhail/cub-4489-cube-dbt-generate`](https://github.com/cube-js/cube/tree/mikhail/cub-4489-cube-dbt-generate)

Comment thread rust/cube-cli/src/commands/dbt.rs Outdated
The comment claimed every exit emits a document; several still do not — the
wait above it, the path checks, and the write loop. A comment stating an
invariant the code lacks is worse than no comment, since the next edit
trusts it.

Narrowed to the two exits it does cover, and it now says why the others are
left: the wait matches `sync --wait`, and a write failure leaves the tree
PARTIALLY written, where `outcome: "failed"` would be a lie. Telling that
apart needs an outcome this command does not have, which is not worth
inventing on speculation.

Also restores the grammar the human line lost when `status` became a token:
`differs` / `is missing` on the printed line, plain tokens in the JSON.
@claude

claude Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Claude finished @MikeNitsenko's task in 1m 25s —— View job


Re-review of 92702ad: both prior findings fixed — 1 low new. Nothing blocking; the manual-run box is the last item.

Full review

  • Gather context (new commit diff)
  • Read rust/cube-cli/src/commands/dbt.rs in full
  • Check prior review threads (all 9 already resolved by the author)
  • Post findings (1 inline comment)

Prior round, verified fixed

Prior finding Fix Verdict
Comment claimed an invariant ("every exit emits exactly one --json document") the code doesn't hold Narrowed to "the two failures BELOW", with a second paragraph naming the two exits deliberately left out and why (:1017–1024) Fixed the right way — the why for the write loop is the part that matters: outcome: "failed" over a partially-written tree would be a lie, and the document has no outcome for "some files landed"
" {} {status}" printed …/orders.yml missing phrase maps missingis missing for the human line only; differing[].status stays the bare token (:1075–1086) Fixed

On leaving the write-loop failures silent under --json: I agree with the reasoning in your reply. failed_json() there would assert files: [] while files are on disk, and a caller acting on it would skip a git add it needs — a partial outcome carrying what was written is the honest shape, and inventing it without a caller asking for it is the worse trade. The comment now records that, which is what keeps the next edit from "fixing" it.

Unchanged and still right: --out as project root, resolve_within's lexical-only check, both pre-wait guards, resolving every path before the first write, and read_generated_files treating a missing content as a protocol error.

New this round

🟡 Low — dbt.rs:1078–1086: the new three-line comment restates the three lines under it. let phrase = if *status == "missing" { "is missing" } else { "differs" } already says that the printed line reads as a sentence while the stored token stays a token — there's no decision here a later edit could undo without noticing. Same hunk, a match would also close the else arm, which today maps any status token added later to "differs".

Comments, generally

Two of the three comments touched this round earn their place: the failed_json block and generate_json's differing keying both state contracts a reader can't recover from the code. The failed_json one runs eight lines against the repo's three-line guideline — but the second paragraph is precisely what the last round asked for, and I can't state "a write failure leaves the tree partially written, so failed would be a lie" in materially less. Leaving it.

Not verified: cargo is present in this job but running it isn't permitted, so cargo test dbt:: / clippy / fmt are unrun on my side — findings read off control flow. The PR body reports them clean on +1.90.0. The description's unchecked "manual run against a deployment on the merged server build" remains the one thing I'd want ticked before merge: the --json document shape and the "an answer, empty or not, ends the poll" rule are both settled in seconds by one real run.

Resolved no threads — the author had already resolved all 9 from the prior rounds, and each is genuinely addressed.

· branch [`mikhail/cub-4489-cube-dbt-generate`](https://github.com/cube-js/cube/tree/mikhail/cub-4489-cube-dbt-generate)

Comment thread rust/cube-cli/src/commands/dbt.rs Outdated
The comment said what the three lines under it already said, and recorded
nothing a later edit could silently undo. The `if/else` it explained also
mapped any status token added later to "differs"; a match with `other =>
other` closes that and is shorter than what it replaces.
@claude

claude Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Claude finished @MikeNitsenko's task in 39s —— View job


Re-review of 11060b5: the last finding is fixed, nothing new. No open threads; the manual-run box is the only item left.

Full review

  • Gather context (new commit diff)
  • Read rust/cube-cli/src/commands/dbt.rs in full
  • Check prior review threads (all 10 already resolved by the author)
  • Post findings (none this round — no inline comments)

Prior round, verified fixed

Prior finding Fix Verdict
Three-line comment restating the phrase mapping under it; else arm mapped any future status token to "differs" Comment gone, if/elsematch *status { "missing" => "is missing", other => other } (dbt.rs:1077–1080) Fixed exactly as suggested — and a net −4 lines, which is the right shape for a comment finding

The other => other arm is the part that matters beyond the comment: a status token added later now prints itself rather than silently claiming differs, and differing[].status in the JSON document stays the bare token either way.

Re-checked this round, unchanged and still right

  • --out as the project root, with resolve_within doing a lexical-only check (absolute rejected, every non-Normal/CurDir component rejected, empty rejected) — and every path resolved before the first std::fs::write, so a rejected path can't leave a half-written tree.
  • Both pre-wait guards (ensure_nothing_was_committed, verify_manifest_source), catching a server doing something other than what was asked in seconds rather than after the 30-minute wait.
  • read_generated_files treating an absent content as a protocol error naming the path, rather than writing a zero-byte file over a committed one.
  • The --json contract and the comment that bounds it: two failing exits below the wait emit outcome: "failed"; the wait and the write loop deliberately don't, with the reason ("failed" over a partially-written tree would be a lie) recorded where the next edit will read it.
  • fetch_generated_files: an answer, empty or not, ends the poll; only absence is "not yet" — and the empty-files bail names the cause that actually reaches it.
  • 25 tests in dbt::, including the three that cover this feature's load-bearing behaviour (generate payload asks for files and never a branch; a returned branch name is refused; hostile paths refused).

Not verified: cargo is present in this job but running it isn't permitted, so cargo test dbt:: / clippy / fmt are unrun on my side — the PR body reports them clean on +1.90.0, and this commit touches three lines inside one else branch. The description's unchecked "manual run against a deployment on the merged server build" is the one thing I'd still want ticked before merge: the --json document shape and the "an answer ends the poll" rule are both settled in seconds by one real run.

Resolved no threads — all 10 from prior rounds were already resolved by the author, and each is genuinely addressed in the current diff.

· branch [`mikhail/cub-4489-cube-dbt-generate`](https://github.com/cube-js/cube/tree/mikhail/cub-4489-cube-dbt-generate)

The integration page taught one topology — Cube converts and commits to a
review branch — and said nothing about the other, which is the whole reason
`cube dbt generate` exists. A customer whose data model lives in their own
Git repository had no way to find it.

Adds the choice where automated syncs are introduced, framed by the question
that actually decides it (who commits the cubes), and a section with a
complete CI job. The job takes the manifest as an artifact rather than from a
dbt project checked out beside it, since that is the shape people will
actually have: no dbt repository anywhere near the runner.

Also covers `--check` as the PR gate, including what it does not catch — a
cube orphaned by a deleted dbt model — so the page does not promise more than
the command does.
@claude

claude Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Claude finished @MikeNitsenko's task in 1m 58s —— View job


Re-review of 198601f (docs only): 1 medium, 2 low. No code changed; all prior code threads remain fixed.

Full review

  • Gather context (new commit diff)
  • Read docs-mintlify/docs/integrations/dbt.mdx in full
  • Cross-check doc claims against dbt.rs and reference/cli.mdx
  • Check prior review threads (all 10 resolved, none re-opened by this commit)
  • Post findings (3 inline comments)

What changed

docs-mintlify/docs/integrations/dbt.mdx only, +88/−4. No Rust touched, so everything verified in the 11060b5 round still stands.

Findings

🟠 Medium — dbt.mdx:521–529: the "complete job" doesn't run as written. Two things: the Cube CLI is never installed (ubuntu-latest has no cube binary, so the generate step is command not found), and permissions: contents: write is missing, so git push 403s under the default read-only GITHUB_TOKEN. cli.mdx's CI-gate sample gets away with both because it's an explicit steps: fragment on a page with an ## Installation section; this one is billed as complete, and the page it's on never mentions installing the CLI.

🟡 Low — dbt.mdx:541: git add model/cubes/dbt hard-codes the default Output path two paragraphs after the page says that setting is what decides where cubes land. A deployment with a different output path stages nothing, the step prints no changes, and the job goes green while the working copy diverges — a false green on the step that matters.

🟡 Low — dbt.mdx:548–550: the sample is on: [push], the prose around it reasons about pull requests. The "no review notification fires — which matters when the job runs on every pull request" argument belongs with the --check variant below, where nothing is committed and the run really is per-PR.

What's right

  • The framing is the best thing here. Opening the section with who commits the generated cubes is the question that actually decides the topology, and putting it at ## Keep Cube in sync automatically — where a reader already is when they start thinking about automation — means the person with their data model in their own repo finds it without knowing the command's name. "Repository webhooks and Analytics Chat always work this way" is accurate against the rest of the page.
  • The --check limitation is stated, not hedged. "not one left behind by a dbt model that was deleted, which neither --check nor the write path removes" matches the code exactly: the check loop walks only server-returned files, and the write loop never deletes. This is the page promising precisely what the command does.
  • Manifest-as-artifact is the right shape for the example — it's the case the command exists for (no dbt repo near the runner), and the inline comment on the download-artifact step says so in one line rather than a paragraph.
  • The <Note> draws the distinction a reader will otherwise get wrong: a deployment id and API key are needed; the dbt repository is not. Anchors check out — #generate-cubes-without-committing-them and #configure-pull-settings both exist.
  • git diff --cached --quiet && echo "no changes" && exit 0 is safe under Actions' bash -e: the failing command is a non-final operand of an && list, so set -e doesn't fire.

Worth noting, not a finding: the --out paragraph, the <Note>, and the --check paragraph are near-verbatim copies of cli.mdx#generate-cubes-without-committing-them. For an integration page that should read standalone that's a defensible duplication — but it's now two places to keep true, and the --check limitation is exactly the sentence that will drift if the write path ever learns to prune.

Not verified: no code in this commit, so no build/test was called for. The PR description's "manual run against a deployment on the merged server build" box is still the one outstanding item from the code rounds.

Resolved no threads — all 10 from prior rounds were already resolved by the author, and this commit doesn't re-open any.

· branch [`mikhail/cub-4489-cube-dbt-generate`](https://github.com/cube-js/cube/tree/mikhail/cub-4489-cube-dbt-generate)

Comment thread docs-mintlify/docs/integrations/dbt.mdx
Comment thread docs-mintlify/docs/integrations/dbt.mdx Outdated
Comment thread docs-mintlify/docs/integrations/dbt.mdx Outdated
Three review findings, all real.

The job was billed as complete and could not run: `ubuntu-latest` has no
`cube` binary, and `GITHUB_TOKEN` is read-only by default, so the step that
generates and the step that pushes both failed. Adds the install step and
`permissions: contents: write`.

`git add model/cubes/dbt` hard-coded the default Output path two paragraphs
after saying that setting decides where cubes land. On a deployment using a
different one it stages nothing, `git diff --cached --quiet` succeeds, and
the job goes green over a working copy that drifted — a false green on the
only step that matters. `git add -A` instead; the CLI writes nowhere else.

The sample triggers on push and commits, but the sentence under it argued
about pull requests. Moved the notification point down to the `--check`
variant, which is the one that runs per PR and commits nothing.
@claude

claude Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Claude finished @MikeNitsenko's task in 1m 29s —— View job


Re-review of 3b19975 (docs only): all 3 prior findings fixed — 1 low new. Nothing blocking.

Full review

  • Gather context (new commit diff)
  • Read the changed section of docs-mintlify/docs/integrations/dbt.mdx in full
  • Verify the install command and anchors against the repo
  • Check prior review threads (all 13 already resolved by the author)
  • Post findings (1 inline comment)

Prior round, verified fixed

Prior finding Fix Verdict
The "complete job" couldn't run: no cube binary, and git push would 403 permissions: contents: write on the job (:524–526) and an Install the Cube CLI step (:534–535) Fixed. The install line matches reference/cli.mdx#installation verbatim and install-cli.sh exists at that path in master; it falls back to $HOME/.local/bin when /usr/local/bin isn't writable, and both are on a hosted runner's PATH
git add model/cubes/dbt hard-coded the default Output path — a false green on a deployment configured with a different one git add -A (:551) Fixed
Sample was on: [push] while the prose under it reasoned about pull requests Notification point moved down to sit with --check (:582–584), and the paragraph under the sample narrowed to "no branch is created in your Cube data model" Fixed the right way — the every-PR frequency it invokes is true of --check and only of --check

Also checked and fine: the commit-back-on-push job doesn't loop, since a push made with GITHUB_TOKEN doesn't trigger another workflow run; checkout@v4 on a push event leaves a real branch, so the bare git push works; git diff --cached --quiet && echo … && exit 0 is still safe under set -e.

New this round

🟡 Low — dbt.mdx:547–551: four lines of rationale inside a snippet the reader copies into their own repo. What it argues against is the review round that produced the line rather than something a later edit of their workflow could reintroduce, and it lands in their file. One clause carries it: the CLI writes only under the Output path, so -A avoids hard-coding it. Same comment, worth noting: -A also stages anything else a reader's job leaves untracked, which the hard-coded path didn't — still the right default for a sample that can't know the output path, but a shorter comment doesn't oversell it.

Unchanged and still right

No Rust in this commit, so everything verified through 11060b5 stands: --out as project root, resolve_within's lexical-only check, both pre-wait guards, resolve-everything-before-writing, read_generated_files treating an absent content as a protocol error, and the bounded --json contract with the reason for its two deliberate gaps recorded where the next edit will read it.

On the page itself: the framing by who commits the generated cubes remains the best thing here, the --check limitation is stated rather than hedged and matches the code, and both anchors (#generate-cubes-without-committing-them, #configure-pull-settings) resolve.

Not verified: no code changed, so no build or test was called for. The PR description's "manual run against a deployment on the merged server build" box is still the one outstanding item from the code rounds.

Resolved no threads — all 13 from prior rounds were already resolved by the author, and this commit doesn't re-open any.

· branch [`mikhail/cub-4489-cube-dbt-generate`](https://github.com/cube-js/cube/tree/mikhail/cub-4489-cube-dbt-generate)

Comment thread docs-mintlify/docs/integrations/dbt.mdx Outdated
The four lines argued against the review round that produced them, not
against anything a later edit of the reader's own workflow could
reintroduce — and this snippet is copied verbatim into their file. One clause
carries what they need to know.
@claude

claude Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Claude finished @MikeNitsenko's task in 27s —— View job


Re-review of e516b6d (docs only): prior finding fixed, nothing new. No open threads — the manual-run box is the last item.

Full review

  • Gather context (new commit diff)
  • Read the changed section of docs-mintlify/docs/integrations/dbt.mdx in full
  • Check prior review threads (all 14 already resolved by the author)
  • Post findings (none this round — no inline comments)

Prior round, verified fixed

Prior finding Fix Verdict
Four lines of rationale inside a snippet the reader copies verbatim into their own repo, arguing against the review round that produced it Two lines: "The CLI writes only under the deployment's Output path, so -A avoids hard-coding it here." (dbt.mdx:547–548), net −2 Fixed as suggested. It keeps the one clause a reader needs to decide whether -A is right for their job, and drops the false-green narration — which was our history, not theirs

Nothing else changed: the commit is 2 insertions, 4 deletions, in one snippet comment.

Re-checked this round, unchanged and still right

The CI sample still runs as written — permissions: contents: write, the install-cli.sh step matching reference/cli.mdx#installation, download-artifact supplying the manifest with no dbt project on the runner, and git diff --cached --quiet && echo … && exit 0 safe under Actions' bash -e (non-final operand of an && list). The --check paragraph still states its one-directional limit rather than claiming the stronger property, and both anchors resolve.

No Rust in this commit, so everything verified through 11060b5 stands: --out as the project root, resolve_within's lexical-only check with every path resolved before the first write, both pre-wait guards, read_generated_files treating an absent content as a protocol error, and the bounded --json contract with the reason for its two deliberate gaps recorded where the next edit will read it.

Not verified: no code changed, so no build or test was called for. The PR description's "manual run against a deployment on the merged server build" box is the one outstanding item from the code rounds.

Resolved no threads — all 14 from prior rounds were already resolved by the author, and this commit doesn't re-open any.

· branch [`mikhail/cub-4489-cube-dbt-generate`](https://github.com/cube-js/cube/tree/mikhail/cub-4489-cube-dbt-generate)

@MikeNitsenko
MikeNitsenko merged commit a746ed3 into master Sep 15, 2026
23 checks passed
@MikeNitsenko
MikeNitsenko deleted the mikhail/cub-4489-cube-dbt-generate branch September 15, 2026 08:38
AvilaJulio added a commit to AvilaJulio/cube that referenced this pull request Sep 15, 2026
…eference

The command table lists one row per top-level command, with each command's
subcommands packed into its Description cell — where they were listed at
all. A reader scanning the Command column for a command they had not seen
before cannot find `enable-branch` there, even though it was added to the
`data-model` cell in cube-js#11433.

Keep that table as a flat index of top-level commands, and add subcommand
tables below it grouped by domain: authentication, deployment, GitHub,
data model, dbt, workspace content, users and access control,
administration, embedding, and agents/app. Each hierarchical row in the
index links to its group, and the narrative sections link into the same
tables rather than repeating them.

Every subcommand is taken from `cube <command> --help` (CLI 1.7.39), so
the tables match what the binary reports, including third-level ones
(`notifications recipients`, `integrations tokens`, `embed tenant`,
`attributes values`, `scim users`/`groups`) that the page did not mention
at all. This also restores `exit-dev-mode` and `file-hashes`, both used by
the Git workflow section but missing or unexplained before. The dbt table
covers `dbt generate` and the `--manifest` sync option from cube-js#11845 and
cube-js#11864, and links to the section that describes them.

The `enable-branch` prose gets its own heading so the tables have an
anchor to link to.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Issues that require a documentation improvement javascript Pull requests that update Javascript code rust Pull requests that update Rust code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants