Skip to content

fix: poll deploy status live so a rollback's actual progress is visible - #452

Merged
thegdsks merged 1 commit into
mainfrom
fix/deploy-status-live-polling
Sep 14, 2026
Merged

fix: poll deploy status live so a rollback's actual progress is visible#452
thegdsks merged 1 commit into
mainfrom
fix/deploy-status-live-polling

Conversation

@thegdsks

@thegdsks thegdsks commented Sep 14, 2026

Copy link
Copy Markdown
Member

Summary

recordInstantDeployAttempt (internal/api/deploys.go) marks a plain image-tag redeploy, rollback, or promote as succeeded the instant its desired-state write lands, before the application controller's reconcile loop has run even once. Two frontend consequences, both silent:

  • deployAttemptsQueryOptions only polls while attempts[0].status === 'running', which is already false by the time the trigger's response comes back for this path, so it never polls at all.
  • deployStatusQueryOptions (the actual reconcile conditions) had no refetchInterval whatsoever.

Net effect: for the most common redeploy/rollback action, the roll-out stage and DeployInProgressBanner could sit on stale, pre-convergence state indefinitely. An operator had no way to tell whether a rollback actually landed without manually reloading the page, and the "Deploy in progress" banner never appeared at all for this path (it was gated on attempt.status === 'running', which this path skips entirely).

Adds useDeployProgress, a hook combining both queries with the one signal that actually reflects "still converging" for an already-finished instant attempt: computeDeployStages' own rollout-stage fallback (status 'running' when no condition transition has landed at or after the attempt's finished_at). Polls both queries every 3s while that holds, stops once the rollout stage lands on done/failed/skipped/unknown. Wired into the three routes that render deploy progress (overview, deploy history, deploy detail logs), replacing their direct useDeployAttempts+useDeployStatus calls. Also fixed overview.tsx's DeployInProgressBanner gate to check the computed stage instead of raw attempt status, so it now actually shows during that window.

Verification

  • New useDeployProgress.test.tsx: proves polling continues while the rollout stage hasn't converged, stops once it has, and never starts at all when the latest attempt already converged on first load.
  • npx tsc -b, npx eslint src, full npx vitest run (158 tests, 31 files) all pass.
  • npm run build succeeds.

What this doesn't do

  • Doesn't change recordInstantDeployAttempt's own "succeeded means the write landed" semantics on the backend: the deploy_attempts row still reports succeeded immediately, which is arguably correct (the desired-state write did succeed) - this is a frontend freshness fix, not a backend status-model change.
  • Doesn't add a hard timeout to the polling: it keeps polling for as long as computeDeployStages reports 'running', matching deployAttemptsQueryOptions' own existing "poll while running, no cap" precedent for a build in progress.

Summary by CodeRabbit

  • New Features
    • Deployment progress now refreshes automatically while rollout stages are still running.
    • Deployment history, logs, and overview pages share consistent deployment attempt and status information.
  • Bug Fixes
    • The deployment-in-progress indicator now remains visible until all rollout stages have converged, including cases where deployment status alone may appear complete.
    • Progress polling stops automatically once deployment convergence is reached.

recordInstantDeployAttempt (internal/api/deploys.go) marks a plain
image-tag redeploy, rollback, or promote as 'succeeded' the instant its
desired-state write lands, before the application controller's
reconcile loop has run even once. Two frontend consequences, both
silent:

- deployAttemptsQueryOptions only polls while attempts[0].status ===
  'running', which is already false by the time the trigger's response
  comes back for this path, so it never polls at all.
- deployStatusQueryOptions (the actual reconcile conditions) had no
  refetchInterval whatsoever.

Net effect: for the most common redeploy/rollback action, the roll-out
stage and DeployInProgressBanner could sit on stale, pre-convergence
state indefinitely. An operator had no way to tell whether a rollback
actually landed without manually reloading the page, and the "Deploy in
progress" banner never appeared at all for this path (it was gated on
attempt.status === 'running', which this path skips entirely).

Adds useDeployProgress, a hook combining both queries with the one
signal that actually reflects "still converging" for an already-
finished instant attempt: computeDeployStages' own rollout-stage
fallback (status 'running' when no condition transition has landed at
or after the attempt's finished_at). Polls both queries every 3s while
that holds, stops once the rollout stage lands on done/failed/skipped/
unknown. Wired into the three routes that render deploy progress
(overview, deploy history, deploy detail logs), replacing their direct
useDeployAttempts+useDeployStatus calls. Also fixed overview.tsx's
DeployInProgressBanner gate to check the computed stage instead of raw
attempt status, so it now actually shows during that window.

What this doesn't do:
- Doesn't change recordInstantDeployAttempt's own "succeeded means the
  write landed" semantics on the backend: the deploy_attempts row still
  reports 'succeeded' immediately, which is arguably correct (the
  desired-state write did succeed) - this is a frontend freshness fix,
  not a backend status-model change.
- Doesn't add a hard timeout to the polling: it keeps polling for as
  long as computeDeployStages reports 'running', matching
  deployAttemptsQueryOptions' own existing "poll while running, no cap"
  precedent for a build in progress.
@github-actions github-actions Bot added the size/l 200-499 lines changed label Sep 14, 2026
@thegdsks
thegdsks merged commit cce8eef into main Sep 14, 2026
7 of 8 checks passed
@thegdsks
thegdsks deleted the fix/deploy-status-live-polling branch September 14, 2026 04:11
@coderabbitai

coderabbitai Bot commented Sep 14, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: b9a816f9-4009-418a-9737-c885413a21ee

📥 Commits

Reviewing files that changed from the base of the PR and between 911d56e and c75a653.

📒 Files selected for processing (5)
  • web/src/hooks/useDeployProgress.test.tsx
  • web/src/hooks/useDeployProgress.ts
  • web/src/routes/apps/$name/deploys/$deployId/logs.tsx
  • web/src/routes/apps/$name/deploys/index.tsx
  • web/src/routes/apps/$name/overview.tsx

📝 Walkthrough

Walkthrough

The PR adds useDeployProgress, which combines deployment queries and polls while deploy stages remain running. Deployment routes use the hook. The overview banner now reflects computed convergence state. Tests cover polling and already-converged deployments.

Changes

Deployment progress

Layer / File(s) Summary
Polling hook and validation
web/src/hooks/useDeployProgress.ts, web/src/hooks/useDeployProgress.test.tsx
useDeployProgress returns attempts and conditions, refetches both queries every three seconds while stages are running, and stops after convergence. Tests cover both polling and already-converged states.
Deployment route integration
web/src/routes/apps/$name/deploys/...
The deployment logs and history routes use useDeployProgress while retaining loader query options.
Overview convergence state
web/src/routes/apps/$name/overview.tsx
The overview computes progress from deploy stages and shows DeployInProgressBanner while any stage remains running.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  participant RouteComponent
  participant useDeployProgress
  participant QueryClient
  participant DeployQueries
  RouteComponent->>useDeployProgress: request deployment progress
  useDeployProgress->>DeployQueries: read attempts and conditions
  useDeployProgress->>QueryClient: refetch status and attempts every 3 seconds
  QueryClient->>DeployQueries: fetch updated deployment data
  DeployQueries-->>useDeployProgress: return attempts and conditions
  useDeployProgress-->>RouteComponent: return deployment progress
Loading
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/deploy-status-live-polling

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sonarqubecloud

Copy link
Copy Markdown

@greptile-apps

greptile-apps Bot commented Sep 14, 2026

Copy link
Copy Markdown

RetriggerConfidence Score: 4/5

The PR should not merge until persistent readiness-crash conditions stop being displayed and polled as an actively progressing rollout.

Findings

  1. P1 Readiness failures poll forever

Summary

  • Updates the overview banner to use computed build and rollout stages rather than only the attempt status.
  • Adds tests for polling during convergence and stopping after a recognized successful condition.
  • Leaves persistent readiness-crash conditions classified as running, resulting in incorrect status and unbounded polling.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Load latest deploy attempt and conditions] --> B[Compute rollout stage]
  B -->|Pending or terminal| C[Do not start progress interval]
  B -->|Running| D[Poll attempts and conditions every 3 seconds]
  D --> E{New reconcile reason}
  E -->|Deployed or AlreadyRunning| F[Stage done; stop polling]
  E -->|Recognized failure| G[Stage failed; stop polling]
  E -->|OOMKilledDuringReadiness or ExitedDuringReadiness| H[Fallback remains running]
  H --> D
Loading

Reviews (1) · Last reviewed commit: "fix: poll deploy status live so a rollba..."

const latestAttempt = attemptsQuery.data[0]
const stillConverging =
latestAttempt !== undefined &&
computeDeployStages(latestAttempt, conditionsQuery.data, true)[1].status === 'running'

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Readiness failures poll forever

When an instant redeploy or rollback repeatedly fails because the container exits or is OOM-killed during readiness, the controller reports ExitedDuringReadiness or OOMKilledDuringReadiness. Neither reason is classified as a rollout failure, so computeDeployStages keeps returning running. This hook consequently polls both resources every three seconds indefinitely while the UI incorrectly presents the failed rollout as still progressing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/frontend web/ size/l 200-499 lines changed type/fix Bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant