[pull] master from cube-js:master#605
Merged
Merged
Conversation
* feat(cube-cli): add native Rust CLI for the Cube Cloud public API New single-binary CLI (`cube`) under rust/cube-cli, structured after the Railway CLI: one module per command group, shared rustls-based HTTP client, multi-context TOML config (~/.config/cube/config.toml, 0600) with CUBE_API_URL/CUBE_API_KEY env and flag overrides. Covers every endpoint of the Console Server public API: deployments, environments and tokens, env vars, folders, workbooks, reports, workspace, notifications and recipients, users, groups, user attributes, resource policies, tenant settings, embed sessions/tenants, OAuth integrations and user tokens, OIDC token configs, agents, AI Engineer, app config/theme, meta, SCIM v2, plus a raw `cube api` escape hatch and shell completions. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdcyqFwUF9BbWLeX4uERnk * feat(cube-cli): add OAuth device-flow login Implement `cube login` via the OAuth 2.0 device authorization grant (RFC 8628), Railway-CLI style: request a device/user code, print the verification URL, open the browser, and poll the token endpoint through authorization_pending/slow_down until approved. Store the resulting access and refresh tokens in the context config. Endpoints, client_id, scope, and optional client_secret are overridable via CUBE_OAUTH_* env vars. `--api-key` remains as a non-interactive fallback. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdcyqFwUF9BbWLeX4uERnk * fix(cube-cli): align device-flow login with DeviceOAuthController contract Point the device flow at the real console-server endpoints: - POST /auth/device/code (initiation) and POST /auth/device/token (poll), base /auth/device, instead of the placeholder /auth/oauth2/* paths. - Treat cube-cli as a public client: send no client_secret; let the server default the scope to all OAUTH_SCOPES when omitted. - Accept the controller's camelCase accessToken/refreshToken (with snake_case aliases for RFC 8628 tolerance). Verified end-to-end against a mock implementing the controller lifecycle (pending -> approve -> single-use mint), including using the minted bearer token on /api/v1/users/me. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdcyqFwUF9BbWLeX4uERnk * fix(cube-cli): send Content-Length: 0 on bodyless writes Bodyless POST/PUT/PATCH/DELETE requests (e.g. `deployments token`, `reports refresh`) were sent with no Content-Length header, which Google's frontend rejects with 411 Length Required. reqwest omits the header for an empty body, so set it explicitly alongside an empty body. Verified against real Cube Cloud staging: the request that previously failed with 411 now reaches the origin normally. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdcyqFwUF9BbWLeX4uERnk * feat(cube-cli): deployment create/update/delete + regions Add the deployment lifecycle commands backed by the new public REST endpoints: - deployments create (POST /api/v1/deployments) with flags for name, region, cloud-provider, target-platform, managed/unmanaged, and creation-step, plus --data for the full CreateDeploymentInput. - deployments update (PUT /api/v1/deployments/:id) and delete (DELETE /api/v1/deployments/:id). - regions (GET /api/v1/regions) to discover valid --region values. Also drop the removed ai-engineer active-region command. Verified end-to-end against Cube Cloud staging: regions -> create -> update -> delete, with a follow-up get confirming cleanup (404). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdcyqFwUF9BbWLeX4uERnk * feat(cube-cli): auto-refresh expired access tokens On a 401, the API client transparently exchanges the saved refresh token at /auth/oauth2/refresh (OAuth refresh_token grant), persists the new token pair back to the active config context, and retries the request once — so a saved login survives the 1-hour access-token TTL without re-authenticating. A dead refresh token falls back to a clear 'session expired' message. Auto-refresh is disabled when an explicit --token/CUBE_API_KEY is supplied. Verified against a mock: expired token -> refresh -> retry -> success with the rotated pair persisted, and dead-refresh-token -> clean 401. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdcyqFwUF9BbWLeX4uERnk * feat(cube-cli): deployments create --bootstrap (scaffold + build) Add --bootstrap/-b to `deployments create`, which targets the build-pod endpoint POST /build/api/v1/deployments. That path scaffolds a project and enqueues the first build, so the deployment serves immediately — unlike the base POST /api/v1/deployments, which only creates the row. Verified end-to-end against Cube Cloud staging: bootstrap-create a deployment, connect it to the demo-db ecom Postgres via env-vars, and query it live (orders.count returned real rows). Flag routing covered by a mock. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdcyqFwUF9BbWLeX4uERnk * feat(cube-cli): data-model file commands Add `cube data-model` (alias `dm`) for managing a deployment's data model files via the DataModelPublicController endpoints: - list [--content] [--branch] -> GET /build/api/v1/deployments/:id/data-model/files - get <path> -> GET (withContent, client-side filter) - put <path> --file/--content -> PUT create/overwrite - delete <paths...> -> DELETE - rename <from> <to> -> POST .../rename Request bodies use { files:[{path,content}] } / { paths:[...] } / { from,to } with an optional branchName; pending confirmation against the live endpoint. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdcyqFwUF9BbWLeX4uERnk * fix(cube-cli): walk data-model file tree; branchName as query param The data-model files endpoint returns a nested tree ({data:[{path,type, content,children}]}); list/get now flatten it depth-first instead of only reading the top level, so nested files (e.g. /model/cubes/orders.yml) list and print correctly. Send branchName as a query parameter on write/rename too, matching GET. Verified against staging: list and get work end-to-end through the CLI. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdcyqFwUF9BbWLeX4uERnk * feat(cube-cli): data-model branch, dev-mode, commit, pull commands Add CLI commands for the branch workflow needed to edit a Git-backed data model (master is read-only): branches (list), create-branch (optionally entering dev mode), dev-mode / exit-dev-mode, commit (commit + push the active branch), and pull. Backed by the new /build/api/v1/deployments/:id/ branches, /dev-mode, /commit, /pull endpoints. Request DTOs pending live verification against staging. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdcyqFwUF9BbWLeX4uERnk * fix(cube-cli): dev-mode requires a branch name The POST /dev-mode endpoint rejects requests without a branch ("Branch name is required"), so make `data-model dev-mode <id> <branch>` a required argument instead of an option. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdcyqFwUF9BbWLeX4uERnk * fix(cube-cli): data-model delete uses files:[{path}] DTO The delete endpoint validates `files` as an array of objects, not a `paths` string array. Send [{path}] per file. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdcyqFwUF9BbWLeX4uERnk * fix(cube-cli): send data-model write branch as branchName body field The write endpoints (PUT/DELETE/rename /data-model/files) resolve the target branch from a branchName body field (falling back to the active dev-mode branch), not a query param. Move --branch into the request body for writes; GET keeps branchName as a query parameter. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdcyqFwUF9BbWLeX4uERnk * docs(cube-cli): document data-model commands Verified end-to-end against staging: explored the ecom schema via an information_schema introspection cube (data-model put + query), authored real cubes (users, orders, line_items, products, product_categories, suppliers) with joins and measures, and confirmed live queries return real data (orders.count=10000, multi-hop joins across users/products). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdcyqFwUF9BbWLeX4uERnk * feat(cube-cli): data-model merge and merge-to-default Add merge (into parent branch; --squash/--switch-to-parent/--delete-branch) and merge-to-default (straight into the deploy branch; -m message, --keep-branch) backed by the new /merge and /merge-to-default endpoints. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdcyqFwUF9BbWLeX4uERnk * docs(cube-cli): describe every command argument in --help Add doc comments to all command arguments (positionals like <DEPLOYMENT> and previously-undescribed flags), so `--help` prints a description for every argument, not just the command. Improve the generic pagination/data descriptions (first/after/offset/limit/data) and normalize doc-before-attr ordering; cargo fmt. The deployment token command already exists as `cube deployments token <id>` (POST /api/v1/deployments/:id/token → cubeApiToken). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdcyqFwUF9BbWLeX4uERnk * ci(cube-cli): add build/test CI and cross-platform release workflows - cube-cli.yml: on changes under rust/cube-cli, run fmt --check, clippy -D warnings, tests, and a release build. - cube-cli-release.yml: on cube-cli-v* tags (or manual dispatch), build a single static binary per platform on native runners (no cross/Docker needed thanks to rustls/musl) and publish archives + SHA256SUMS as GitHub release assets. Targets: x86_64/aarch64-unknown-linux-musl (static), x86_64/aarch64-apple- darwin, x86_64-pc-windows-msvc. Verified the musl target builds a 5.6M statically-linked single binary locally. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdcyqFwUF9BbWLeX4uERnk * ci(cube-cli): release from the main Release workflow, version follows Cube Instead of a standalone workflow + cube-cli-v* tags, build and publish the CLI from the existing Release workflow (publish.yml, on v*.*.* tags): - Add a cube-cli matrix job that builds a single static binary per platform (x86_64/aarch64 linux-musl, x86_64/aarch64 macos, x86_64 windows) and attaches them to the same GitHub release as Cube, via svenstaro/upload-release-action (matching cubestore/native jobs). - The job overrides the crate version from the pushed tag, and Cargo.toml is pinned to the current Cube version (1.7.2), so `cube --version` always matches the Cube release. - Remove the standalone cube-cli-release.yml; keep cube-cli.yml for PR CI. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdcyqFwUF9BbWLeX4uERnk * ci(cube-cli): build+publish CLI from main Release workflow; version follows Cube Add a cube-cli matrix job to publish.yml (v*.*.* tags) that builds a single static binary per platform and attaches it to the same GitHub release as Cube via svenstaro/upload-release-action. The job sets the crate version from the pushed tag; Cargo.toml is pinned to the current Cube version (1.7.2) so `cube --version` matches the Cube release. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdcyqFwUF9BbWLeX4uERnk * fix(cube-cli): address PR review — token file race, windows browser, refresh errors - config: write config.toml via a 0600 temp file + atomic rename, closing the world-readable window between fs::write and set_permissions (and avoiding a truncated token file on a crash mid-write). - oauth: open the browser on Windows with rundll32 FileProtocolHandler instead of `cmd /C start`, which treats `&` in the verification URL as a command separator. - client: log the underlying refresh error instead of silently reporting the original 401 as 'session expired' for non-expiry failures. - output: drop the always-true guard in items(). - cube-cli CI: add an explicit `permissions: contents: read` block (CodeQL). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdcyqFwUF9BbWLeX4uERnk * style(cube-cli): rustfmt the config atomic-write change * ci(cube-cli): build and test across release targets on PRs Split the single-host lint-and-test job into a lint job (fmt + clippy) and a cross-platform build-and-test matrix that mirrors the release targets in publish.yml (linux musl x86_64/aarch64, macOS Intel/ARM, windows-msvc). Each PR now compiles and runs the CLI test suite on every target it releases for, so a broken cross-platform build is caught in review instead of at tag time. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdcyqFwUF9BbWLeX4uERnk --------- Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
See Commits and Changes for more details.
Created by
pull[bot] (v2.0.0-alpha.4)
Can you help keep this open source service alive? 💖 Please sponsor : )