fix(services): deployment timer - #2903
Conversation
|
View your CI Pipeline Execution ↗ for commit 192fd55
💡 Verify your cache is correct by running tasks in a sandbox. Read docs ↗ ☁️ Nx Cloud last updated this comment at |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## staging #2903 +/- ##
===========================================
+ Coverage 48.49% 48.52% +0.03%
===========================================
Files 1303 1304 +1
Lines 28117 28128 +11
Branches 8215 8220 +5
===========================================
+ Hits 13634 13648 +14
+ Misses 12187 12186 -1
+ Partials 2296 2294 -2
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
b11a3b2 to
0e3772e
Compare
- Use generated service step metrics for ongoing duration tracking - Bump qovery-typescript-axios to 1.1.961
78d9fab to
6045774
Compare
- Show completed deployment duration from total metrics - Include elapsed time for ongoing deployment steps
|
@cubic-dev-ai Review this PR please |
@rmnbrd I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
Pull request overview
This PR standardizes how deployment step durations are computed and displayed in the Service Logs UI, ensuring ongoing deployments tick locally based on backend started_at data and completed deployments show consistent totals.
Changes:
- Introduces shared helpers to compute per-step and aggregate durations (including live elapsed time for ongoing steps).
- Updates the deployment log header and the Build/Deploy/Executing stage filters to use these shared duration helpers and a 1s tick via
useIntervalTick. - Adds unit and component tests covering completed, ongoing, invalid/missing timestamps, future starts, and aggregation behavior.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| libs/domains/service-logs/feature/src/lib/service-step-metrics.ts | Adds shared helper functions for step-level and aggregate duration calculation. |
| libs/domains/service-logs/feature/src/lib/service-step-metrics.spec.ts | Adds unit tests for duration helpers across edge cases. |
| libs/domains/service-logs/feature/src/lib/list-deployment-logs/filters-stage-step/filters-stage-step.tsx | Uses shared duration helpers + useIntervalTick to keep stage durations live and consistent. |
| libs/domains/service-logs/feature/src/lib/list-deployment-logs/filters-stage-step/filters-stage-step.spec.tsx | Adds regression tests validating live ticking from started_at and completed duration behavior. |
| libs/domains/service-logs/feature/src/lib/header-logs/header-logs.tsx | Switches header duration computation to shared helpers and prefers total_duration_sec when completed. |
| libs/domains/service-logs/feature/src/lib/header-logs/header-logs.spec.tsx | Adds tests for completed deployment totals and live ongoing aggregation/ticking. |
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| useIntervalTick(isOngoing) | ||
|
|
||
| const totalDurationSec = | ||
| isOngoing && serviceStatus?.last_deployment_date | ||
| ? Math.floor((Date.now() - new Date(serviceStatus.last_deployment_date).getTime()) / 1000) | ||
| : serviceStatus?.steps?.total_computing_duration_sec ?? 0 | ||
| const totalDurationSec = isOngoing | ||
| ? getServiceStepsDurationSec(serviceStatus.steps?.details ?? [], Date.now()) | ||
| : serviceStatus.steps?.total_duration_sec ?? serviceStatus.steps?.total_computing_duration_sec ?? 0 |
There was a problem hiding this comment.
2 issues found across 6 files
Confidence score: 3/5
- In
libs/domains/service-logs/feature/src/lib/header-logs/header-logs.tsx, the ongoing-deployment timer depends entirely on populatedsteps.details; missing or incomplete timing fields can make the header show an incorrect near-zero duration. Add a reliable fallback for ongoing deployments. - In
libs/domains/service-logs/feature/src/lib/service-step-metrics.ts, an ONGOING step with a validduration_secbut malformedstarted_atis reduced to0, underreporting metrics; preserve the recorded duration when the start timestamp is invalid.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="libs/domains/service-logs/feature/src/lib/header-logs/header-logs.tsx">
<violation number="1" location="libs/domains/service-logs/feature/src/lib/header-logs/header-logs.tsx:59">
P2: For an ongoing deployment, the new timer derives entirely from `steps.details`. If those details are empty or lack a populated ongoing `started_at`/`duration_sec`, `getServiceStepsDurationSec` returns ~0 and the header freezes there, whereas the previous logic measured from `last_deployment_date` and always showed real elapsed time. Consider falling back to the `last_deployment_date`-based elapsed time when the step aggregation yields no live/recorded duration.</violation>
</file>
<file name="libs/domains/service-logs/feature/src/lib/service-step-metrics.ts">
<violation number="1" location="libs/domains/service-logs/feature/src/lib/service-step-metrics.ts:7">
P3: An ONGOING step with a valid backend `duration_sec` but a malformed `started_at` returns `0` instead of the recorded duration, because the `Number.isFinite` branch hardcodes `0`. This is inconsistent with the `!step.started_at` branch, which falls back to `duration_sec`, and can regress the 'frozen at last backend duration' symptom this PR fixes. Fall back to `step.duration_sec || 0` when the parse fails.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Fix all with cubic | Re-trigger cubic
| ? Math.floor((Date.now() - new Date(serviceStatus.last_deployment_date).getTime()) / 1000) | ||
| : serviceStatus?.steps?.total_computing_duration_sec ?? 0 | ||
| const totalDurationSec = isOngoing | ||
| ? getServiceStepsDurationSec(serviceStatus.steps?.details ?? [], Date.now()) |
There was a problem hiding this comment.
P2: For an ongoing deployment, the new timer derives entirely from steps.details. If those details are empty or lack a populated ongoing started_at/duration_sec, getServiceStepsDurationSec returns ~0 and the header freezes there, whereas the previous logic measured from last_deployment_date and always showed real elapsed time. Consider falling back to the last_deployment_date-based elapsed time when the step aggregation yields no live/recorded duration.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At libs/domains/service-logs/feature/src/lib/header-logs/header-logs.tsx, line 59:
<comment>For an ongoing deployment, the new timer derives entirely from `steps.details`. If those details are empty or lack a populated ongoing `started_at`/`duration_sec`, `getServiceStepsDurationSec` returns ~0 and the header freezes there, whereas the previous logic measured from `last_deployment_date` and always showed real elapsed time. Consider falling back to the `last_deployment_date`-based elapsed time when the step aggregation yields no live/recorded duration.</comment>
<file context>
@@ -54,10 +55,9 @@ export function HeaderLogs({
- ? Math.floor((Date.now() - new Date(serviceStatus.last_deployment_date).getTime()) / 1000)
- : serviceStatus?.steps?.total_computing_duration_sec ?? 0
+ const totalDurationSec = isOngoing
+ ? getServiceStepsDurationSec(serviceStatus.steps?.details ?? [], Date.now())
+ : serviceStatus.steps?.total_duration_sec ?? serviceStatus.steps?.total_computing_duration_sec ?? 0
</file context>
| if (step.status !== 'ONGOING' || !step.started_at) return step.duration_sec || 0 | ||
|
|
||
| const startedAtMs = Date.parse(step.started_at) | ||
| return Number.isFinite(startedAtMs) ? Math.max(0, Math.floor((nowMs - startedAtMs) / 1_000)) : 0 |
There was a problem hiding this comment.
P3: An ONGOING step with a valid backend duration_sec but a malformed started_at returns 0 instead of the recorded duration, because the Number.isFinite branch hardcodes 0. This is inconsistent with the !step.started_at branch, which falls back to duration_sec, and can regress the 'frozen at last backend duration' symptom this PR fixes. Fall back to step.duration_sec || 0 when the parse fails.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At libs/domains/service-logs/feature/src/lib/service-step-metrics.ts, line 7:
<comment>An ONGOING step with a valid backend `duration_sec` but a malformed `started_at` returns `0` instead of the recorded duration, because the `Number.isFinite` branch hardcodes `0`. This is inconsistent with the `!step.started_at` branch, which falls back to `duration_sec`, and can regress the 'frozen at last backend duration' symptom this PR fixes. Fall back to `step.duration_sec || 0` when the parse fails.</comment>
<file context>
@@ -0,0 +1,12 @@
+ if (step.status !== 'ONGOING' || !step.started_at) return step.duration_sec || 0
+
+ const startedAtMs = Date.parse(step.started_at)
+ return Number.isFinite(startedAtMs) ? Math.max(0, Math.floor((nowMs - startedAtMs) / 1_000)) : 0
+}
+
</file context>
| return Number.isFinite(startedAtMs) ? Math.max(0, Math.floor((nowMs - startedAtMs) / 1_000)) : 0 | |
| return Number.isFinite(startedAtMs) ? Math.max(0, Math.floor((nowMs - startedAtMs) / 1_000)) : step.duration_sec || 0 |
Goal
Keep deployment duration displays accurate and consistent while a deployment is progressing and after it completes.
Previously, ongoing timers could remain frozen at the last backend duration or measure from the wrong timestamp. The deployment logs header also displayed computing time after completion, while the deployment history displayed the full duration.
Changes
started_attimestamp and refresh it locally every second.total_duration_secin the completed deployment header, with computing duration as a compatibility fallback.useIntervalTickhook to make the live-refresh behavior explicit and ensure interval cleanup.