Skip to content

Add brev launchable create and brev launchable deploy subcommands #391

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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions