feat: add releases ls, and fix commands invoke and deploy#63
Conversation
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 SummaryThis PR wires up three CLI commands end-to-end in platform mode: a new
Confidence Score: 5/5Safe 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.
|
| 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
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
| 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, | ||
| }), |
There was a problem hiding this 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.
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.
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 soalien commands invokecan reach the manager, and a fix soalien deploycan provision. The manager gains aGET /v1/releasesto back the new command.What's in here:
alien releases ls— lists a project's releases (ID, created, commit, platforms), backed by a newGET /v1/releaseson the manager.commands invokefix — 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 waydeploymentsdoes.deployfix — 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 unblockerstack_settingsis 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 viaalien dev).alien deploy --platform local→ gets past the workspace-conversion error and the sync-lock failure, and provisions.--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:
Subject's debug output redacts the bearer.releases lsreturns 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.