Skip to content
This repository was archived by the owner on Jan 19, 2026. It is now read-only.

Commit aa4b1fa

Browse files
committed
refactor(progress-display): enhance progress tracking with elapsed time for events
1 parent 7c81816 commit aa4b1fa

2 files changed

Lines changed: 67 additions & 9 deletions

File tree

src/commands/components/push/graph-operations/resource-processor.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,14 +192,18 @@ async function processNode(
192192
force: boolean,
193193
): Promise<NodeProcessingResult> {
194194
const node = graph.nodes.get(nodeId)!;
195+
// Track start time for individual process timing
196+
const startTime = Date.now();
195197

196198
try {
197199
// Skip if resource is already up-to-date (unless force is enabled)
198200
if (!force && node.shouldSkip()) {
201+
const elapsedMs = Date.now() - startTime;
199202
progressDisplay.handleEvent({
200203
type: 'skip',
201204
name: node.getName(),
202205
resourceType: getResourceTypeName(node.type),
206+
elapsedMs,
203207
});
204208
return { name: node.getName(), skipped: true };
205209
}
@@ -208,21 +212,25 @@ async function processNode(
208212
const result = await node.upsert(space);
209213
node.updateTargetData(result);
210214

215+
const elapsedMs = Date.now() - startTime;
211216
progressDisplay.handleEvent({
212217
type: 'success',
213218
name: node.getName(),
214219
resourceType: getResourceTypeName(node.type),
215220
color: getResourceTypeColor(node.type),
221+
elapsedMs,
216222
});
217223

218224
return { name: node.getName(), skipped: false };
219225
}
220226
catch (error) {
227+
const elapsedMs = Date.now() - startTime;
221228
progressDisplay.handleEvent({
222229
type: 'error',
223230
name: node.getName(),
224231
resourceType: getResourceTypeName(node.type),
225232
error,
233+
elapsedMs,
226234
});
227235
return { name: node.getName(), skipped: false, error };
228236
}

src/commands/components/push/progress-display.ts

Lines changed: 59 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ import { konsola } from '../../../utils/konsola';
33

44
export type ProcessingEvent =
55
| { type: 'start'; total: number }
6-
| { type: 'success'; name: string; resourceType: string; color: string }
7-
| { type: 'skip'; name: string; resourceType: string }
8-
| { type: 'error'; name: string; resourceType: string; error: unknown }
6+
| { type: 'success'; name: string; resourceType: string; color: string; elapsedMs?: number }
7+
| { type: 'skip'; name: string; resourceType: string; elapsedMs?: number }
8+
| { type: 'error'; name: string; resourceType: string; error: unknown; elapsedMs?: number }
99
| { type: 'complete'; summary: { updated: number; unchanged: number; failed: number } };
1010

1111
export class ProgressDisplay {
@@ -15,13 +15,18 @@ export class ProgressDisplay {
1515
private unchanged = 0;
1616
private failed = 0;
1717
private currentProgressLine = '';
18+
// Track start time for calculating elapsed time on completion
19+
private startTime: number | null = null;
1820

1921
start(total: number) {
2022
this.total = total;
2123
this.processed = 0;
2224
this.updated = 0;
2325
this.unchanged = 0;
2426
this.failed = 0;
27+
// Record the start time when processing begins
28+
this.startTime = Date.now();
29+
konsola.br();
2530
console.log(`Processing ${total} resources...`);
2631
this.updateProgress();
2732
}
@@ -32,33 +37,49 @@ export class ProgressDisplay {
3237
this.start(event.total);
3338
break;
3439

35-
case 'success':
40+
case 'success': {
3641
this.processed++;
3742
this.updated++;
3843
this.clearProgress();
39-
console.log(`${chalk.green('✓')} ${this.capitalize(event.resourceType)}${chalk.hex(event.color)(event.name)} - Updated`);
44+
const successTimeString = event.elapsedMs ? chalk.dim(` (${this.formatElapsedTime(event.elapsedMs)})`) : '';
45+
console.log(`${chalk.green('✓')} ${this.capitalize(event.resourceType)}${chalk.hex(event.color)(event.name)} - Updated${successTimeString}`);
4046
this.updateProgress();
4147
break;
48+
}
4249

43-
case 'skip':
50+
case 'skip': {
4451
this.processed++;
4552
this.unchanged++;
53+
// Optionally show timing for skipped items if they take longer than expected
54+
const skipTimeString = event.elapsedMs && event.elapsedMs > 10 ? chalk.dim(` (${this.formatElapsedTime(event.elapsedMs)})`) : '';
55+
if (skipTimeString) {
56+
this.clearProgress();
57+
console.log(`${chalk.dim('—')} ${this.capitalize(event.resourceType)}${chalk.dim(event.name)} - Skipped${skipTimeString}`);
58+
}
4659
this.updateProgress();
4760
break;
61+
}
4862

49-
case 'error':
63+
case 'error': {
5064
this.processed++;
5165
this.failed++;
5266
this.clearProgress();
53-
console.log(`${chalk.red('✗')} ${this.capitalize(event.resourceType)}${chalk.red(event.name)} - Failed`);
67+
const errorTimeString = event.elapsedMs ? chalk.dim(` (${this.formatElapsedTime(event.elapsedMs)})`) : '';
68+
console.log(`${chalk.red('✗')} ${this.capitalize(event.resourceType)}${chalk.red(event.name)} - Failed${errorTimeString}`);
5469
this.updateProgress();
5570
break;
71+
}
5672

5773
case 'complete': {
5874
this.clearProgress();
5975
const { updated, unchanged, failed } = event.summary;
60-
konsola.ok(`Completed: ${updated} updated, ${unchanged} unchanged, ${failed} failed`);
6176

77+
// Calculate elapsed time if startTime was recorded
78+
const elapsedTime = this.startTime ? Date.now() - this.startTime : null;
79+
const timeString = elapsedTime ? this.formatElapsedTime(elapsedTime) : '';
80+
81+
konsola.ok(`Completed: ${updated} updated, ${unchanged} unchanged, ${failed} failed${timeString ? ` in ${timeString}` : ''}`, true);
82+
konsola.br();
6283
// Show summary of skipped items when there are many
6384
if (unchanged > 5) {
6485
console.log(chalk.dim(` (${unchanged} resources were already up-to-date)`));
@@ -82,6 +103,35 @@ export class ProgressDisplay {
82103
private capitalize(str: string): string {
83104
return str.charAt(0).toUpperCase() + str.slice(1);
84105
}
106+
107+
/**
108+
* Formats elapsed time in milliseconds to a human-readable string
109+
* @param ms - Time in milliseconds
110+
* @returns Formatted time string (e.g., "1.2s", "2m 30s", "1h 5m")
111+
*/
112+
private formatElapsedTime(ms: number): string {
113+
if (ms < 1000) {
114+
return `${ms}ms`;
115+
}
116+
117+
const seconds = Math.floor(ms / 1000);
118+
const minutes = Math.floor(seconds / 60);
119+
const hours = Math.floor(minutes / 60);
120+
121+
if (hours > 0) {
122+
const remainingMinutes = minutes % 60;
123+
return remainingMinutes > 0 ? `${hours}h ${remainingMinutes}m` : `${hours}h`;
124+
}
125+
126+
if (minutes > 0) {
127+
const remainingSeconds = seconds % 60;
128+
return remainingSeconds > 0 ? `${minutes}m ${remainingSeconds}s` : `${minutes}m`;
129+
}
130+
131+
// For times under a minute, show decimal precision for seconds
132+
const preciseSeconds = (ms / 1000).toFixed(1);
133+
return `${preciseSeconds}s`;
134+
}
85135
}
86136

87137
// Global display instance - single source of truth for terminal output

0 commit comments

Comments
 (0)