Skip to content

feat: add releases ls, and fix commands invoke and deploy#63

Merged
alongubkin merged 8 commits into
mainfrom
itamar/alien-148-cli-platform-mode-commands
Jun 1, 2026
Merged

feat: add releases ls, and fix commands invoke and deploy#63
alongubkin merged 8 commits into
mainfrom
itamar/alien-148-cli-platform-mode-commands

Conversation

@ItamarZand88
Copy link
Copy Markdown
Contributor

Summary

A few changes so the core CLI commands work end-to-end once you're logged in to a workspace: a new alien releases ls, a fix so alien commands invoke can reach the manager, and a fix so alien deploy can provision. The manager gains a GET /v1/releases to back the new command.

What's in here:

  • alien releases ls — lists a project's releases (ID, created, commit, platforms), backed by a new GET /v1/releases on the manager.
  • commands invoke fix — it built its manager connection the standalone-only way, so once you were logged in to a hosted workspace it had nothing to talk to. It now resolves the manager the same way deployments does.
  • deploy fix — provisioning passed the workspace id where the API expects the workspace name, and used the install token for the manager's sync step (which needs the deployment's own token). Both fixed. ← the e2e unblocker
  • A supporting refactor: a deployment record's stack_settings is now optional, so a record built from a list summary (which doesn't carry them) is representable. The manager's own stores always set it; execution paths assert it.

How I tested

Local stack, locally-built CLI (cargo build -p alien-cli --features platform), fresh login:

  • alien releases ls → shows the release I published.
  • alien commands invoke --deployment <name> --command where-am-i → returns the command result (also confirmed via alien dev).
  • alien deploy --platform local → gets past the workspace-conversion error and the sync-lock failure, and provisions.
  • Builds clean with and without --features platform; manager builds with the OpenAPI feature.

I also ran a security review on the diff — the deploy change is the only auth-sensitive part. Things it checked:

  • The deployment's own token is used only for that deployment's own provisioning — not another deployment, not a wider scope.
  • The workspace value comes from the caller's own resolved workspace, not from anything attacker-supplied; a missing one fails fast with a validation error instead of falling through.
  • No token reaches the logs — the Subject's debug output redacts the bearer.
  • releases ls returns only the releases the caller is allowed to read, so the list can't surface one you couldn't already fetch by id.

Nothing came up.

Matches the optional fields next to it. A caller that builds a record without stack settings leaves it absent instead of inserting an empty default that would serialize as if it were real. Stores set it; execution paths expect it.
commands invoke used server_sdk_client(), which errors in platform mode, and a token-only commands client that drops the workspace header. It now resolves the manager like deployments and reuses that workspace-aware client.
Adds list_releases to the ReleaseStore trait, its sqlite implementation (newest first), the GET handler, and the OpenAPI registration. Returns only releases the caller may read. The CLI command and SDK regeneration land separately.
Adds the list_releases path and ListReleasesResponse schema via the schema exporter + openapi-down-convert. The progenitor build.rs picks these up to generate the typed list_releases() client method. No other spec changes — it was otherwise in sync.
Lists releases newest-first through the resolved manager, mirroring deployments ls (ID, created, commit, platforms). Available in normal and dev modes.
…ng a deploy

Provisioning passed the workspace id where the deploy APIs select a
workspace by name, and used the install token for the manager's sync
step, which needs the deployment's own token. Both blocked onboarding.

- create_deployment / get_deployment_group now receive the workspace
  name from whoami; fail fast if it is missing.
- the provisioning client re-authenticates as the deployment (its own
  token holds managers.sync for itself) in platform mode; standalone
  is unchanged.
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Jun 1, 2026

Greptile Summary

This PR wires up three CLI commands end-to-end in platform mode: a new alien releases ls command backed by GET /v1/releases on the manager, a fix to commands invoke so it discovers the manager URL via the same workspace-aware resolution path used by deployments, and a fix to deploy that corrects the workspace identifier passed to the platform API and uses the deployment's own token (not the install token) for the manager sync step.

  • alien releases ls — new GET /v1/releases handler with per-item authz filtering; SQLite store returns all rows (single-tenant, filtered at the route layer); new ListReleasesResponse schema added to OpenAPI.
  • commands invoke fix — drops server_sdk_client() + bare auth-token approach; resolves manager via resolve_project / resolve_manager and passes the resulting http_client (which already carries the workspace header) to CommandsClient.
  • deploy fixworkspace_idworkspace_name in the create_deployment call and in fetch_deployment_group; provisioning now re-authenticates with the deployment's own token when a workspace is present; DeploymentRecord.stack_settings made Option<StackSettings> so list-only records are representable, with stores wrapping in Some and HTTP handlers returning 500 on None.

Confidence Score: 5/5

Safe to merge — all three targeted bug fixes are correct and well-scoped, and the optional stack_settings refactor is consistently applied across stores and handlers.

The workspace_id→name fix, deployment-token re-auth, and manager-resolution rewrite all address clearly identified bugs with matching test coverage in the author's local e2e run. The stack_settings Option refactor is applied consistently: stores always wrap in Some, HTTP handlers return 500 on None (replacing the expect calls that triggered the previous review comments), and the deployment loop retains expect under its catch_unwind. The only asymmetry is in the command registry, which uses expect in a path reachable from HTTP handlers — a minor inconsistency worth addressing but not a blocker.

crates/alien-manager/src/stores/sqlite/command_registry.rs — uses expect rather than ok_or_else in an HTTP-reachable path, unlike the consistent approach taken in sync.rs and stack.rs.

Important Files Changed

Filename Overview
crates/alien-cli/src/commands/commands.rs Fixes commands invoke in platform mode by resolving the manager via resolve_project/resolve_manager instead of server_sdk_client; switches CommandsClient from token-string to http_client for workspace-header propagation.
crates/alien-cli/src/commands/deploy.rs Two fixes: workspace_name replaces workspace_id in the create_deployment call, and in platform mode the provisioning client is rebuilt with the deployment's own token rather than the install token.
crates/alien-cli/src/deployment_tracking.rs DeploymentToken::DeploymentGroup now carries workspace_name instead of workspace_id; extraction from whoami response includes an explicit validation error when the field is absent.
crates/alien-manager/src/routes/releases.rs Adds GET /v1/releases backed by list_releases; per-item authz filter is applied; route is co-registered on the existing /v1/releases path alongside POST.
crates/alien-manager/src/routes/sync.rs Replaced expect calls on stack_settings in the agent_sync handler with proper 500 error returns; aligns with the same fix applied to stack.rs.
crates/alien-manager/src/routes/stack.rs Both stack_import branches now unwrap stack_settings with an explicit 500 response rather than expect; comparison in import_changes_deployment correctly handles the new Option type.
crates/alien-manager/src/stores/sqlite/command_registry.rs Adapted to Option using expect; unlike sync.rs/stack.rs, this path is reachable from HTTP command handlers without catch_unwind protection.
crates/alien-manager/src/traits/deployment_store.rs stack_settings changed to Option with serde skip; clearly documented that stores always set it.
crates/alien-cli/src/commands/releases.rs New releases ls command; delegates manager resolution to the shared resolve_manager_client helper; displays ID, created-at, commit ref, and platform columns.

Sequence Diagram

sequenceDiagram
    participant CLI
    participant Platform API
    participant Manager

    Note over CLI,Manager: alien commands invoke (fixed)
    CLI->>Platform API: resolve_project() → project_id
    CLI->>Platform API: GET /v1/resolve?platform=aws&project=…
    Platform API-->>CLI: manager_url + workspace header
    CLI->>Manager: list_deployments (http_client with workspace header)
    Manager-->>CLI: deployment_id
    CLI->>Manager: CommandsClient.invoke (same http_client)
    Manager-->>CLI: result

    Note over CLI,Manager: alien deploy (fixed)
    CLI->>Platform API: validate_token → workspace_name
    CLI->>Platform API: GET /v1/workspaces/{workspace_name}/deployment-groups/{id}
    Platform API-->>CLI: deployment group details
    CLI->>Platform API: POST /v1/workspaces/{workspace_name}/deployments
    Platform API-->>CLI: deployment token
    CLI->>Manager: GET /v1/resolve → manager_url
    CLI->>Manager: sync (re-authed with deployment token)
    Manager-->>CLI: sync OK

    Note over CLI,Manager: alien releases ls (new)
    CLI->>Manager: GET /v1/releases (workspace-aware client)
    Manager->>Manager: list_releases (SQLite, all rows)
    Manager->>Manager: per-item can_read_release filter
    Manager-->>CLI: "ListReleasesResponse{items}"
    CLI-->>CLI: display table
Loading
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
crates/alien-manager/src/stores/sqlite/command_registry.rs:149-156
**`expect` in HTTP-reachable code may panic and drop connections**

`create_command` is called from the alien-commands HTTP layer. If `stack_settings` is ever `None` here (e.g., a record introduced by a future code path), `.expect(…)` will panic inside an async task that is not covered by the deployment loop's `catch_unwind`, dropping the HTTP connection instead of returning a structured error. The PR already replaced the same pattern with `ok_or_else(|| …)?` in `sync.rs` and `stack.rs`; applying the same treatment here would make this path consistent.

Reviews (2): Last reviewed commit: "fix(manager): return 500 from sync/stack..." | Re-trigger Greptile

Comment thread crates/alien-manager/src/routes/sync.rs Outdated
Comment on lines 273 to 280
Json(StackImportResponse {
deployment_id: updated.id,
deployment_token: updated.deployment_token,
stack_settings: updated.stack_settings,
stack_settings: updated
.stack_settings
.expect("imported deployment carries stack_settings"),
stack_state,
}),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 expect in HTTP handler causes panic instead of error response

stack_import is an Axum HTTP handler. The two .expect("imported/created deployment carries stack_settings") calls (lines 273–280 and 353–358) will panic if the invariant is broken — for example, if stack_import is ever extended to update a record that was loaded from a list endpoint rather than the full-record store path. A panic in a handler drops the connection; returning a proper 500 InternalServerError via ok_or_else(|| …)? would be safer and more debuggable.

Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/alien-manager/src/routes/stack.rs
Line: 273-280

Comment:
**`expect` in HTTP handler causes panic instead of error response**

`stack_import` is an Axum HTTP handler. The two `.expect("imported/created deployment carries stack_settings")` calls (lines 273–280 and 353–358) will panic if the invariant is broken — for example, if `stack_import` is ever extended to update a record that was loaded from a list endpoint rather than the full-record store path. A panic in a handler drops the connection; returning a proper `500 InternalServerError` via `ok_or_else(|| …)?` would be safer and more debuggable.

How can I resolve this? If you propose a fix, please make it concise.

…ttings

The sync and stack-import HTTP handlers asserted the stack_settings
invariant with .expect(). Axum doesn't catch handler panics, so a broken
invariant would drop the connection instead of answering. Return a
structured 500 instead. The internal deployment loop and stores keep
.expect() — the loop already isolates a panicking tick.
@alongubkin alongubkin merged commit a8c811e into main Jun 1, 2026
13 of 15 checks passed
@alongubkin alongubkin deleted the itamar/alien-148-cli-platform-mode-commands branch June 1, 2026 14:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants