Skip to content

Add brev launchable create and brev launchable deploy subcommands #391

@theFong

Description

@theFong

Summary

There is no command-line path to create a Brev Launchable or to deploy one into a workspace. Today these flows only exist in the web console (brev.dev/launchable/...). For anything scripted — CI publishing a launchable from a repo, agent harnesses spinning up reproducible demo VMs, automated smoke tests — operators have to either drive the Brev API by hand with curl or build a private wrapper. Both are fragile, both require knowing the org-scoped REST routes, and both reinvent error handling.

This issue asks for two new subcommands under a brev launchable group:

  • brev launchable create — POST to /api/organizations/:id/v2/launchables with a payload built from local files (the same shape the wizard sends).
  • brev launchable deploy — provision a workspace from an existing launchable id (the same flow https://brev.dev/launchable/deploy/now?launchableID=… triggers when a user clicks it).

Reproduction (today)

$ brev launchable --help
Error: unknown command "launchable" for "brev"

$ brev start "https://brev.dev/launchable/deploy/now?launchableID=env-..."
panic: invalid launchable URL: …

goroutine 1 [running]:
github.com/brevdev/brev-cli/pkg/cmd/start.MakeNewWorkspaceFromURL({0x16b48321b, 0x53})
    /go/src/github.com/brevdev/brev-cli/pkg/cmd/start/start.go:561 +0x4cc
github.com/brevdev/brev-cli/pkg/cmd/start.maybeStartFromGitURL(0x14000225c80, …)
    /go/src/github.com/brevdev/brev-cli/pkg/cmd/start/start.go:217 +0x7c
…

brev start does have machinery for launchable URLs (the binary's string table contains Deploying launchable: %q, failed to fetch launchable %q: %w, invalid launchable URL: %w, /launchable/deploy/now?launchableID=), but the call path panics on any URL the user supplies in this form.

Working around it currently requires either the web console or hand-crafted API calls. Examples of routes I had to discover by reading strings output on the binary:

  • POST /api/organizations/:org/v2/launchables (create — works)
  • POST /api/organizations/:org/v2/launchables/edit (works, with id, drop viewAccess)
  • POST /api/organizations/:org/workspaces (returns BadRequestError: Legacy workspace version unsupported)
  • POST /api/users/me/workspaces, /api/launchables/:id/deploy, /api/launchables/:id/launch, /api/v2/workspaces, etc. — all 404

Whatever the web frontend at brev.dev/launchable/deploy/now calls to actually provision a VM is not surfaced anywhere a CLI user can find it.

What we'd like

brev launchable create

Read a payload from a path (or stdin) and POST it to /api/organizations/:org/v2/launchables. On success, print the new launchable's id and shareable URL.

# From a single JSON file containing { name, description, viewAccess,
# createWorkspaceRequest, buildRequest, file? }
brev launchable create --from ./launchable.json

# Or with the lifecycle script broken out (mirrors how the web wizard
# splits them) — the CLI inlines the script into vmBuild.lifeCycleScriptAttr.script.
brev launchable create \
  --name "dex-ui" \
  --description "Browser control plane for OpenShell" \
  --view-access public \
  --instance-type n1-standard-2 \
  --workspace-group GCP \
  --region us-west1 \
  --sub-location us-west1-a \
  --storage 256 \
  --image-id "projects/ubuntu-os-cloud/global/images/family/ubuntu-2404-lts-amd64" \
  --port 3000:dex-ui:secure-link \
  --setup-script ./launchable-setup.sh

Output:

launchable id: env-3Dvdn5kvH5MJ28hKWdtU2GbkC4W
share url:     https://brev.dev/launchable/deploy/now?launchableID=env-3Dvdn5kvH5MJ28hKWdtU2GbkC4W

brev launchable edit and brev launchable list are obvious follow-ups; this issue scopes the MVP to create since that's the entry point.

brev launchable deploy

Provision a new workspace from an existing launchable id. Same flow as clicking the share URL. Should reject any combination that mixes the launchable's create-workspace defaults with conflicting flags rather than silently overriding.

# Default — pull every field from the launchable's createWorkspaceRequest
brev launchable deploy env-3Dvdn5kvH5MJ28hKWdtU2GbkC4W

# With a name + detached so a script can poll separately
brev launchable deploy env-3Dvdn5kvH5MJ28hKWdtU2GbkC4W \
  --name dex-ui-smoketest \
  --detached

Output (detached):

workspace id:   <id>
workspace name: dex-ui-smoketest
state:          DEPLOYING
poll with:      brev ls dex-ui-smoketest

Output (foreground):

[deploy] provisioning n1-standard-2 in us-west1-a…
[deploy] reached BUILDING after 38s
[deploy] reached RUNNING after 2m41s
[deploy] lifecycle script in progress (poll /var/log/brev/oncreate-lifecycle-script-*.log for details)
[deploy] workspace healthy after 6m12s
secure link: https://3000-…brev-prod.ngc.nvidia.com/

Non-goals for this issue

  • Listing org launchables and showing per-launchable launch counts. Trackable separately as brev launchable list / brev launchable show.
  • Authoring launchables with a fully-interactive TUI. The MVP is non-interactive + scriptable.

Why this matters

We just published a Launchable for dex-ui by:

  1. Hand-crafting the JSON payload from API-shape notes (api-shapes.md).
  2. Extracting the operator's Brev access token from ~/.brev/credentials.json and POSTing the create request via curl.
  3. Iterating with POST .../v2/launchables/edit to fix storage units, missing imageId, etc.
  4. Asking the user to click the share URL because we couldn't programmatically launch.

Steps 1–3 should be one command. Step 4 should be the second. Without these, every agent or CI flow that wants to publish + smoke-test a Launchable has to keep reinventing the wheel.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions