Skip to content

[pull] master from cube-js:master#605

Merged
pull[bot] merged 1 commit into
code:masterfrom
cube-js:master
Jul 18, 2026
Merged

[pull] master from cube-js:master#605
pull[bot] merged 1 commit into
code:masterfrom
cube-js:master

Conversation

@pull

@pull pull Bot commented Jul 18, 2026

Copy link
Copy Markdown

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 : )

* 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>
@pull pull Bot locked and limited conversation to collaborators Jul 18, 2026
@pull pull Bot added the ⤵️ pull label Jul 18, 2026
@pull
pull Bot merged commit 7c39841 into code:master Jul 18, 2026
9 of 11 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant