Skip to content

Commit 7084737

Browse files
authored
♻️ Split TDD status plumbing (#263)
Why: merge TDD/status/runtime plumbing into the remaining audit integration base so the final PR can verify the full runtime cleanup together.\n\nWhat changed:\n- Status, doctor, preview, TDD daemon, run, and supporting runtime service cleanup.\n- Removed obsolete service-manager and region-service paths.\n- Added/updated focused command and TDD service tests.\n\nVerification:\n- npm run lint\n- Final verification will happen on the last PR back to main.
1 parent 70de5bb commit 7084737

30 files changed

Lines changed: 2910 additions & 1375 deletions

src/cli.js

Lines changed: 55 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ import {
5555
tddStartCommand,
5656
tddStatusCommand,
5757
tddStopCommand,
58+
validateTddStartOptions,
5859
} from './commands/tdd-daemon.js';
5960
import { uploadCommand, validateUploadOptions } from './commands/upload.js';
6061
import { validateWhoamiOptions, whoamiCommand } from './commands/whoami.js';
@@ -65,6 +66,7 @@ import {
6566
generateStaticReport,
6667
getReportFileUrl,
6768
} from './services/static-report-generator.js';
69+
import { withTimeout } from './utils/async-utils.js';
6870
import { openBrowser } from './utils/browser.js';
6971
import { colors } from './utils/colors.js';
7072
import { loadConfig } from './utils/config-loader.js';
@@ -376,24 +378,21 @@ let plugins = [];
376378
try {
377379
plugins = await loadPlugins(configPath, config);
378380

379-
for (const plugin of plugins) {
381+
for (let plugin of plugins) {
380382
try {
381383
// Add timeout protection for plugin registration (5 seconds)
382-
const registerPromise = plugin.register(program, {
384+
let registerPromise = plugin.register(program, {
383385
config,
384386
services: pluginServices,
385387
output,
386-
// Backwards compatibility alias for plugins using old API
387-
logger: output,
388388
});
389-
const timeoutPromise = new Promise((_, reject) =>
390-
setTimeout(
391-
() => reject(new Error('Plugin registration timeout (5s)')),
392-
5000
393-
)
389+
390+
await withTimeout(
391+
registerPromise,
392+
5000,
393+
'Plugin registration timeout (5s)'
394394
);
395395

396-
await Promise.race([registerPromise, timeoutPromise]);
397396
output.debug(`Registered plugin: ${plugin.name}`);
398397
} catch (error) {
399398
output.warn(`Failed to register plugin ${plugin.name}: ${error.message}`);
@@ -418,15 +417,13 @@ program
418417
.argument('<path>', 'Path to screenshots directory or file')
419418
.option('-b, --build-name <name>', 'Build name for grouping')
420419
.option('-m, --metadata <json>', 'Additional metadata as JSON')
421-
.option('--batch-size <n>', 'Upload batch size', v => parseInt(v, 10))
422-
.option('--upload-timeout <ms>', 'Upload timeout in milliseconds', v =>
423-
parseInt(v, 10)
424-
)
420+
.option('--batch-size <n>', 'Upload batch size', Number)
421+
.option('--upload-timeout <ms>', 'Upload timeout in milliseconds', Number)
425422
.option('--branch <branch>', 'Git branch')
426423
.option('--commit <sha>', 'Git commit SHA')
427424
.option('--message <msg>', 'Commit message')
428425
.option('--environment <env>', 'Environment name', 'test')
429-
.option('--threshold <number>', 'Comparison threshold', parseFloat)
426+
.option('--threshold <number>', 'Comparison threshold', Number)
430427
.option('--token <token>', 'API token override')
431428
.option('--wait', 'Wait for build completion')
432429
.option('--upload-all', 'Upload all screenshots without SHA deduplication')
@@ -461,7 +458,12 @@ tddCmd
461458
.option('--baseline-build <id>', 'Use specific build as baseline')
462459
.option('--baseline-comparison <id>', 'Use specific comparison as baseline')
463460
.option('--environment <env>', 'Environment name', 'test')
464-
.option('--threshold <number>', 'Comparison threshold', parseFloat)
461+
.option('--threshold <number>', 'Comparison threshold', Number)
462+
.option(
463+
'--min-cluster-size <pixels>',
464+
'Minimum changed-pixel cluster size',
465+
Number
466+
)
465467
.option('--timeout <ms>', 'Server timeout in milliseconds', '30000')
466468
.option('--fail-on-diff', 'Fail tests when visual differences are detected')
467469
.option('--token <token>', 'API token override')
@@ -475,6 +477,15 @@ tddCmd
475477
return;
476478
}
477479

480+
let validationErrors = validateTddStartOptions(options);
481+
if (validationErrors.length > 0) {
482+
output.error('Validation errors:');
483+
for (let error of validationErrors) {
484+
output.printErr(` - ${error}`);
485+
}
486+
process.exit(1);
487+
}
488+
478489
await tddStartCommand(options, globalOptions);
479490
});
480491

@@ -512,7 +523,12 @@ tddCmd
512523
.option('--port <port>', 'Port for TDD server', '47392')
513524
.option('--branch <branch>', 'Git branch override')
514525
.option('--environment <env>', 'Environment name', 'test')
515-
.option('--threshold <number>', 'Comparison threshold', parseFloat)
526+
.option('--threshold <number>', 'Comparison threshold', Number)
527+
.option(
528+
'--min-cluster-size <pixels>',
529+
'Minimum changed-pixel cluster size',
530+
Number
531+
)
516532
.option('--token <token>', 'API token override')
517533
.option('--timeout <ms>', 'Server timeout in milliseconds', '30000')
518534
.option('--baseline-build <id>', 'Use specific build as baseline')
@@ -602,6 +618,14 @@ program
602618
.option('--commit <sha>', 'Git commit SHA')
603619
.option('--message <msg>', 'Commit message')
604620
.option('--environment <env>', 'Environment name', 'test')
621+
.option('--threshold <number>', 'Comparison threshold', Number)
622+
.option(
623+
'--min-cluster-size <pixels>',
624+
'Minimum changed-pixel cluster size',
625+
Number
626+
)
627+
.option('--batch-size <n>', 'Upload batch size', Number)
628+
.option('--upload-timeout <ms>', 'Upload timeout in milliseconds', Number)
605629
.option('--token <token>', 'API token override')
606630
.option('--wait', 'Wait for build completion')
607631
.option('--timeout <ms>', 'Server timeout in milliseconds', '30000')
@@ -664,13 +688,8 @@ program
664688
.option('--environment <env>', 'Filter by environment')
665689
.option('-p, --project <slug>', 'Filter by project slug')
666690
.option('--org <slug>', 'Filter by organization slug')
667-
.option(
668-
'--limit <n>',
669-
'Maximum results to return (1-250)',
670-
val => parseInt(val, 10),
671-
20
672-
)
673-
.option('--offset <n>', 'Skip first N results', val => parseInt(val, 10), 0)
691+
.option('--limit <n>', 'Maximum results to return (1-250)', Number, 20)
692+
.option('--offset <n>', 'Skip first N results', Number, 0)
674693
.option('--comparisons', 'Include comparisons when fetching a specific build')
675694
.addHelpText(
676695
'after',
@@ -710,13 +729,8 @@ program
710729
.option('--name <pattern>', 'Search comparisons by name (supports wildcards)')
711730
.option('--status <status>', 'Filter by status (identical, new, changed)')
712731
.option('--branch <branch>', 'Filter by branch (for name search)')
713-
.option(
714-
'--limit <n>',
715-
'Maximum results to return (1-250)',
716-
val => parseInt(val, 10),
717-
50
718-
)
719-
.option('--offset <n>', 'Skip first N results', val => parseInt(val, 10), 0)
732+
.option('--limit <n>', 'Maximum results to return (1-250)', Number, 50)
733+
.option('--offset <n>', 'Skip first N results', Number, 0)
720734
.option('-p, --project <slug>', 'Filter by project slug')
721735
.option('--org <slug>', 'Filter by organization slug')
722736
.addHelpText(
@@ -789,17 +803,17 @@ contextCmd
789803
.option(
790804
'--similar-limit <n>',
791805
'Maximum similar fingerprint matches to return (1-50)',
792-
val => parseInt(val, 10)
806+
Number
793807
)
794808
.option(
795809
'--recent-limit <n>',
796810
'Maximum recent same-name comparisons to return (1-50)',
797-
val => parseInt(val, 10)
811+
Number
798812
)
799813
.option(
800814
'--window-size <n>',
801815
'Historical hotspot analysis window size (1-50)',
802-
val => parseInt(val, 10)
816+
Number
803817
)
804818
.addHelpText(
805819
'after',
@@ -835,12 +849,12 @@ contextCmd
835849
.option(
836850
'--recent-limit <n>',
837851
'Maximum recent comparisons to return (1-50)',
838-
val => parseInt(val, 10)
852+
Number
839853
)
840854
.option(
841855
'--window-size <n>',
842856
'Historical hotspot analysis window size (1-50)',
843-
val => parseInt(val, 10)
857+
Number
844858
)
845859
.addHelpText(
846860
'after',
@@ -873,9 +887,7 @@ contextCmd
873887
.option('--source <source>', 'Context source: auto, cloud, or local', 'auto')
874888
.option('-p, --project <slug-or-id>', 'Project scope for user auth lookups')
875889
.option('--org <slug>', 'Organization slug when project slug is ambiguous')
876-
.option('--limit <n>', 'Maximum matches to return (1-50)', val =>
877-
parseInt(val, 10)
878-
)
890+
.option('--limit <n>', 'Maximum matches to return (1-50)', Number)
879891
.addHelpText(
880892
'after',
881893
`
@@ -905,10 +917,8 @@ contextCmd
905917
.option('--source <source>', 'Context source: auto, cloud, or local', 'auto')
906918
.option('-p, --project <slug-or-id>', 'Project scope for user auth lookups')
907919
.option('--org <slug>', 'Organization slug when project slug is ambiguous')
908-
.option('--limit <n>', 'Maximum comparisons to return (1-100)', val =>
909-
parseInt(val, 10)
910-
)
911-
.option('--offset <n>', 'Skip first N comparisons', val => parseInt(val, 10))
920+
.option('--limit <n>', 'Maximum comparisons to return (1-100)', Number)
921+
.option('--offset <n>', 'Skip first N comparisons', Number)
912922
.addHelpText(
913923
'after',
914924
`
@@ -1179,13 +1189,8 @@ program
11791189
.command('projects')
11801190
.description('List projects you have access to')
11811191
.option('--org <slug>', 'Filter by organization slug')
1182-
.option(
1183-
'--limit <n>',
1184-
'Maximum results to return (1-250)',
1185-
val => parseInt(val, 10),
1186-
50
1187-
)
1188-
.option('--offset <n>', 'Skip first N results', val => parseInt(val, 10), 0)
1192+
.option('--limit <n>', 'Maximum results to return (1-250)', Number, 50)
1193+
.option('--offset <n>', 'Skip first N results', Number, 0)
11891194
.addHelpText(
11901195
'after',
11911196
`
@@ -1240,7 +1245,6 @@ program
12401245
.description('Upload static files as a preview for a build')
12411246
.argument('[path]', 'Path to static files (dist/, build/, out/)')
12421247
.option('-b, --build <id>', 'Build ID to attach preview to')
1243-
.option('-p, --parallel-id <id>', 'Look up build by parallel ID')
12441248
.option('--base <path>', 'Override auto-detected base path')
12451249
.option('--open', 'Open preview URL in browser after upload')
12461250
.option('--dry-run', 'Show what would be uploaded without uploading')

0 commit comments

Comments
 (0)