feat(deploy): per-service deploy queue, cancellable deployments, no-cache builds

- internal/deployqueue: serialized FIFO per service — concurrent deploys
  of one service no longer race (the container-name-conflict failure
  mode); different services still run in parallel.
- Deploys and rollbacks enqueue through the queue; a busy service
  returns status 'queued' and promotes when the active job finishes.
- POST /deployments/:id/cancel — queued jobs drop immediately
  (200 cancelled); active jobs get ctx cancelled so build/reconcile
  abort (202 cancelling, settles 'cancelled'); finished deployments
  return 409 NOT_CANCELLABLE.
- Redeploy and variables save now route through the same queue so they
  can't overlap a build; PUT /services/:id/variables accepts
  redeploy:true to apply env changes to running containers.
- no_cache on deployment create plumbs BuildRequest -> BuildOptions for
  nixpacks, dockerfile and railpack builders.
- CLI: deployments cancel, deploy --no-cache, variables set/unset
  --redeploy. MCP: containr_deployments_cancel; deploy/variables_set
  document no_cache/redeploy. UI: Cancel button + queued/cancelled
  badges on the deployments table. OpenAPI + types regenerated.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Tomas DvorakandDevin committed 2026-10-08 16:23:28 +02:00
1 parent 84d58cf808
commit 7839dc48a3
25 files changed
+721 -32

No files matched your search

+3 -2
View File
@@ -64,15 +64,16 @@ func init() {
// Variables
register(spec{"containr_variables_list", "List service environment variables (secrets masked)", "GET", "/services/{id}/variables", []string{"id"}, nil, false})
register(spec{"containr_variables_set", "Replace all variables on a service. Args: id, variables = [{key, value, is_secret}]. Fetch the current list first and merge — the server replaces wholesale.", "PUT", "/services/{id}/variables", []string{"id"}, []string{"variables"}, false})
register(spec{"containr_variables_set", "Replace all variables on a service. Args: id, variables = [{key, value, is_secret}], redeploy (queue a redeploy to apply). Fetch the current list first and merge — the server replaces wholesale.", "PUT", "/services/{id}/variables", []string{"id"}, []string{"variables", "redeploy"}, false})
// Deployments
register(spec{"containr_deploy", "Trigger a deployment for a service. Args: id (service), commit_hash, branch.", "POST", "/services/{id}/deployments", []string{"id"}, []string{"commit_hash", "branch", "trigger"}, false})
register(spec{"containr_deploy", "Trigger a deployment for a service. Args: id (service), commit_hash, branch, no_cache.", "POST", "/services/{id}/deployments", []string{"id"}, []string{"commit_hash", "branch", "trigger", "no_cache"}, false})
register(spec{"containr_deployments_list", "List recent deployments across services", "GET", "/deployments", nil, nil, false})
register(spec{"containr_services_deployments", "List deployments for one service", "GET", "/services/{id}/deployments", []string{"id"}, nil, false})
register(spec{"containr_deployments_get", "Get a deployment", "GET", "/deployments/{id}", []string{"id"}, nil, false})
register(spec{"containr_deployments_logs", "Get persisted build/runtime logs for a deployment. Args: id, type (all|build|runtime).", "GET", "/deployments/{id}/logs", []string{"id"}, nil, false})
register(spec{"containr_deployments_rollback", "Roll back to a deployment. Requires confirm:true.", "POST", "/deployments/{id}/rollback", []string{"id"}, nil, true})
register(spec{"containr_deployments_cancel", "Cancel a queued or running deployment", "POST", "/deployments/{id}/cancel", []string{"id"}, nil, false})
// Databases
register(spec{"containr_databases_list", "List managed databases", "GET", "/databases", nil, nil, false})