From a4b22702b36f494a322ebe42842a8d8f849a0afe Mon Sep 17 00:00:00 2001 From: Mike Donnalley Date: Fri, 4 Oct 2024 12:57:39 -0600 Subject: [PATCH] fix: show correct time in CI mode --- examples/sf-specific/deploy.ts | 141 +++++++++++++++++++++++++++++++++ src/multi-stage-output.tsx | 6 +- 2 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 examples/sf-specific/deploy.ts diff --git a/examples/sf-specific/deploy.ts b/examples/sf-specific/deploy.ts new file mode 100644 index 0000000..0b8c95e --- /dev/null +++ b/examples/sf-specific/deploy.ts @@ -0,0 +1,141 @@ +import {MultiStageOutput} from '../../src/multi-stage-output.js' + +async function sleep(ms: number): Promise { + return new Promise((resolve) => { + setTimeout(resolve, ms) + }) +} + +type Data = { + mdapiDeploy: any + sourceMemberPolling: any + status: string + message: string + username: string + id: string +} + +function round(value: number, precision: number): number { + const multiplier = 10 ** (precision || 0) + return Math.round(value * multiplier) / multiplier +} + +function formatProgress(current: number, total: number): string { + if (total === 0) { + return '0/0 (0%)' + } + + return `${current}/${total} (${round((current / total) * 100, 0)}%)` +} + +const stages = [ + 'Preparing', + 'Waiting for the org to respond', + 'Deploying Metadata', + 'Running Tests', + 'Updating Source Tracking', + 'Done', +] + +const ms = new MultiStageOutput({ + jsonEnabled: false, + postStagesBlock: [ + { + bold: true, + get: (data): string | undefined => data?.status, + label: 'Status', + type: 'dynamic-key-value', + }, + { + get: (data): string | undefined => data?.id, + label: 'Deploy ID', + neverCollapse: true, + type: 'static-key-value', + }, + { + get: (data): string | undefined => data?.username, + label: 'Target Org', + type: 'static-key-value', + }, + ], + preStagesBlock: [ + { + get: (data): string | undefined => data?.message, + type: 'message', + }, + ], + stageSpecificBlock: [ + { + get: (data): string | undefined => + data?.mdapiDeploy?.numberComponentsTotal + ? formatProgress(data?.mdapiDeploy?.numberComponentsDeployed ?? 0, data?.mdapiDeploy?.numberComponentsTotal) + : undefined, + label: 'Components', + stage: 'Deploying Metadata', + type: 'dynamic-key-value', + }, + { + get: (data): string | undefined => + data?.mdapiDeploy?.numberTestsTotal && data?.mdapiDeploy?.numberTestsCompleted + ? formatProgress(data?.mdapiDeploy?.numberTestsCompleted, data?.mdapiDeploy?.numberTestsTotal) + : undefined, + label: 'Tests', + stage: 'Running Tests', + type: 'dynamic-key-value', + }, + { + get: (data): string | undefined => + data?.sourceMemberPolling?.original + ? formatProgress(data.sourceMemberPolling.remaining, data.sourceMemberPolling.original) + : undefined, + label: 'Members', + stage: 'Updating Source Tracking', + type: 'dynamic-key-value', + }, + ], + stages, + title: 'Deploying Metadata', +}) + +ms.goto('Preparing') +await sleep(400) + +const username = 'test-byyrthcpuxjx@example.com' +ms.updateData({ + id: '0AfKP000000Xugj0AC', + message: `Deploying v61.0 metadata to ${username} using the v62.0 REST API`, + status: 'In Progress', + username, +}) + +ms.goto('Deploying Metadata') + +for (let i = 0; i <= 10; i++) { + ms.updateData({ + mdapiDeploy: { + numberComponentsDeployed: i, + numberComponentsTotal: 10, + }, + }) + + // eslint-disable-next-line no-await-in-loop + await sleep(200) +} + +ms.updateData({status: 'Succeeded'}) +ms.skipTo('Updating Source Tracking') + +for (let i = 0; i <= 10; i++) { + ms.updateData({ + sourceMemberPolling: { + original: 10, + remaining: i, + }, + }) + // eslint-disable-next-line no-await-in-loop + await sleep(100) +} + +ms.goto('Done') + +ms.stop() diff --git a/src/multi-stage-output.tsx b/src/multi-stage-output.tsx index 0cd10bb..d651022 100644 --- a/src/multi-stage-output.tsx +++ b/src/multi-stage-output.tsx @@ -176,9 +176,9 @@ class CIMultiStageOutput> { } case 'current': { + if (!this.startTimes.has(stage)) this.startTimes.set(stage, Date.now()) if (Date.now() - this.lastUpdateTime < this.messageTimeout) break this.lastUpdateTime = Date.now() - if (!this.startTimes.has(stage)) this.startTimes.set(stage, Date.now()) ux.stdout(`${this.design.icons.current.figure} ${stage}…`) this.printInfo(this.preStagesBlock, 3) this.printInfo( @@ -191,6 +191,10 @@ class CIMultiStageOutput> { case 'failed': case 'skipped': + case 'paused': + case 'aborted': + case 'async': + case 'warning': case 'completed': { this.seenStages.add(stage) if (this.hasStageTime && status !== 'skipped') {