Skip to content

v8.10.0

@sorccu sorccu tagged this 24 Jun 18:05
* feat(cli): use the async project deploy API [RED-644]

Switch `checkly deploy` from the synchronous POST /next-v2/projects/deploy to the
async POST /v1/projects/deploy, then poll GET /v1/projects/deployments/{id}/completion
to completion. This removes the API-gateway request-timeout ceiling that caused
large projects to fail with a 504 even though the deploy was still running.

- rest/projects.ts: deploy() submits the deployment and awaits completion
  (looping the long-poll completion endpoint, which 408s while in progress);
  dry runs still return the preview diff synchronously. Adds ProjectDeployment /
  ProjectDeployFailedError types, getDeployment(), and awaitDeploymentCompletion()
  with best-effort progress reporting.
- commands/deploy.ts: show a spinner with live progress during the deploy.

The deploy() return shape ({ data: { project, diff } }) is unchanged for callers.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CjBjJjMihvJKv8PEzuCvKi

* feat(cli): follow deploys via the SSE progress stream [RED-644]

Replace the completion long-poll (which only refreshed progress ~every 30s) with
the backend's Server-Sent Events stream: deploy() now follows
GET /v1/projects/deployments/{id}/events, surfacing each `progress` frame to
onProgress for a smooth bar and resolving on the terminal `complete` frame. A
single authenticated GET — no client polling.

- streamDeploymentEvents() reconnects (bounded) when the stream drops before a
  terminal frame, covering both a clean EOF and a socket error (ECONNRESET) — the
  common mid-deploy interruption; the server is stateless so resuming needs no cursor.
- openEventStream() buffers an HTTP error body (the response is a stream, so the
  interceptor can't classify it) and re-runs the classifier to surface the typed
  error (NotFoundError, etc.).
- Drops the snapshot-on-408 progress hack. dryRun stays the synchronous preview.

Requires the backend SSE route to be deployed first.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CjBjJjMihvJKv8PEzuCvKi

* feat(cli): address deployments under their project [RED-644]

Follow a deploy via /v1/projects/{logicalId}/deployments/{id}/events, threading the
project logicalId (URL-encoded) through the deployment-following calls.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Claude-Session: https://claude.ai/code/session_01CjBjJjMihvJKv8PEzuCvKi

* feat(deploy): add --cancel-in-progress-deployment to preempt an in-flight deploy [RED-644]

When the async deploy endpoint returns 409 (a deployment is already in progress
for the project), the CLI previously errored out. With the new opt-in flag, the
CLI instead cancels the in-flight deployment, waits for it to finish unwinding,
and retries — so a new deploy can preempt an older one instead of waiting.

- New boolean flag --cancel-in-progress-deployment (default false). It is a
  dedicated flag, NOT --force (which only skips the confirmation prompt).
- On a 409 with the flag set, deploy() cancels the specific in-flight
  deployment (from the conflict's deploymentId), long-polls the completion
  endpoint to a final state, then retries — bounded to avoid an unbounded
  cancel war, and treating a vanished predecessor (404) as "slot free".
- awaitDeploymentCompletion floors its poll cadence so a server returning 408
  immediately can't become a tight request loop.
- A "Waiting for an in-progress deployment to finish…" status message is shown
  during the wait; a 409 without the flag now prints an actionable hint.
- rest/errors.ts: type the deploymentId carried on a 409 ErrorData.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CjBjJjMihvJKv8PEzuCvKi

* feat(deploy): expose cancelRequestedAt on the deployment type [RED-644]

Mirror the API response shape: the deployment now carries cancelRequestedAt
(null unless a cancellation has been requested).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CjBjJjMihvJKv8PEzuCvKi

* feat(deploy): wait for an in-progress deployment by default, then deploy [RED-644]

Previously a 409 (a deployment is already in progress for the project) failed the
deploy. Now `checkly deploy` waits for the in-progress deployment to finish and
then deploys — the same flow as --cancel-in-progress-deployment minus the cancel
step. The flag now means "cancel it instead of waiting".

- On a 409, deploy() resolves the conflict and retries: it long-polls the
  completion endpoint until the predecessor reaches a final state (optionally
  cancelling it first with the flag), and only then re-POSTs the deploy — once.
  The payload is never re-uploaded while the predecessor is still running.
- awaitDeploymentCompletion is a single long-poll; the wait/retry cadence lives
  in deploy()/resolveInProgressDeployment, bounded by an overall ~30-min deadline
  after which the 409 surfaces with a clear message.
- A predecessor whose worker died is finalized by the backend reaper, after
  which the completion poll returns and we deploy.
- Update the flag description and the conflict message to match.

Depends on the backend returning 409 immediately on a concurrent deploy
(previously the request hung), without which neither the wait nor the cancel
path could start.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CjBjJjMihvJKv8PEzuCvKi

* fix(deploy): clearly report a cancelled deployment [RED-644]

A deployment that was cancelled surfaced as the generic "Your project could not
be deployed. / The deployment did not complete successfully." — giving no hint
that it was cancelled.

- Add ProjectDeployCancelledError; submitDeployment throws it when the deploy
  stream completes as CANCELLED (a reliable signal, independent of any backend
  error text), before the generic non-SUCCEEDED failure path.
- The deploy command reports it distinctly: title "Your deployment was
  cancelled." with the body "A newer deployment may have cancelled yours. Try
  deploying again if you still need to apply your changes." Still an error
  (❌, exit 1) — only the messaging changes.
- Also reword the in-progress (409 past wait deadline) message so the body
  stands on its own instead of leaning on the title.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CjBjJjMihvJKv8PEzuCvKi

* style(deploy): plain "..." instead of unicode ellipsis in status messages [RED-644]

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CjBjJjMihvJKv8PEzuCvKi

* style(deploy): "an in-progress deployment" in the cancel status message [RED-644]

Matches the wait message; "the" implied a deployment the user hadn't been told about.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CjBjJjMihvJKv8PEzuCvKi

* feat(deploy): type the deploy-result project with id + createdAt/updatedAt

The async/sync deploy response's `result.project` was typed as the bare `Project`
(name/logicalId/repoUrl). Add a `DeployedProject` type matching the API's
deploy-result shape: the `id` it always returned plus the newly added camelCase
`createdAt`/`updatedAt` timestamps.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CjBjJjMihvJKv8PEzuCvKi

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Assets 2
Loading