Stu Mason · Folkestone, Kent
One entryMerged PR· StuMason/coolify-mcp· TypeScript

PR #246 merged: feat: add wait option to deploy tool (#238)

additions
+395
deletions
-8
files changed
5

Summary

Closes #238

deploy used to fire-and-forget: a build could fail (e.g. build container OOM-killed) while the old container kept serving, and the caller never found out — a deployed-looking site that's actually still on stale code.

Adds an opt-in wait: boolean (+ timeout_seconds, default 300) to the deploy tool:

  • Triggers the deploy as before, then polls getDeployment every ~5s until a terminal status (finished / failed / cancelled) or the timeout elapses.
  • Success: returns status, deployment_uuid, commit, timestamps, and duration_seconds.
  • Failure: same, plus a bounded log tail (truncateLogs, last 30 entries / 10KB) — built from essential fields only, never the raw upstream Deployment object (which can carry server/application secrets — see #232).
  • Timeout: current status + timed_out: true + a next_action hint to keep polling via deployment get.
  • No-wait path is byte-for-byte unchanged.

Multi-deployment tags

A tag can match several applications, so Coolify's /deploy response can contain multiple deployment_uuids. This PR watches only the first and lists the rest under additional_deployment_uuids in the response, so the caller can poll them separately via deployment get. Watching all of them concurrently was considered but adds meaningful complexity (parallel polling, combined timeout/status semantics) for a case that's uncommon in practice; the explicit additional_deployment_uuids field keeps the behavior discoverable instead of silent.

Side fix

deployByTagOrUuid was typed as Promise<MessageResponse> ({ message: string }), but Coolify's real /deploy response is { deployments: [{ message, resource_uuid, deployment_uuid }] } (confirmed against docs/openapi-chunks/deployments-api.yaml). This was a latent "wrong-at-runtime" type (same class of bug as #158), and had to be fixed to extract the deployment uuid(s) to poll. New DeployTriggerResponse type added; old { message }-only shape still works for backwards compatibility.

Test mockability

Poll sleeps use an isolated sleep() helper so tests drive them with jest.useFakeTimers() + jest.advanceTimersByTimeAsync() rather than real waits.

Test plan

  • npm test — 369 passed (5 new: success/failure/timeout/no-wait/multi-deployment-first-only)
  • npm run lint — clean (pre-existing warnings only, no errors)
  • npx prettier --check . — clean
  • npm run build — passes
  • Verified failure-path test asserts the response never contains server/application/private_key/env_secret fields from a raw upstream payload

🤖 Generated with Claude Code