@vizzly-testing/cli 0.19.2 → 0.20.1-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/client.js +134 -0
- package/dist/api/core.js +341 -0
- package/dist/api/endpoints.js +314 -0
- package/dist/api/index.js +19 -0
- package/dist/auth/client.js +91 -0
- package/dist/auth/core.js +176 -0
- package/dist/auth/index.js +30 -0
- package/dist/auth/operations.js +148 -0
- package/dist/cli.js +1 -1
- package/dist/client/index.js +0 -1
- package/dist/commands/doctor.js +3 -3
- package/dist/commands/finalize.js +41 -15
- package/dist/commands/login.js +7 -6
- package/dist/commands/logout.js +4 -4
- package/dist/commands/project.js +5 -4
- package/dist/commands/run.js +158 -90
- package/dist/commands/status.js +22 -18
- package/dist/commands/tdd.js +105 -78
- package/dist/commands/upload.js +61 -26
- package/dist/commands/whoami.js +4 -4
- package/dist/config/core.js +438 -0
- package/dist/config/index.js +13 -0
- package/dist/config/operations.js +327 -0
- package/dist/index.js +1 -1
- package/dist/project/core.js +295 -0
- package/dist/project/index.js +13 -0
- package/dist/project/operations.js +393 -0
- package/dist/report-generator/core.js +315 -0
- package/dist/report-generator/index.js +8 -0
- package/dist/report-generator/operations.js +196 -0
- package/dist/reporter/reporter-bundle.iife.js +16 -16
- package/dist/screenshot-server/core.js +157 -0
- package/dist/screenshot-server/index.js +11 -0
- package/dist/screenshot-server/operations.js +183 -0
- package/dist/sdk/index.js +3 -2
- package/dist/server/handlers/api-handler.js +14 -5
- package/dist/server/handlers/tdd-handler.js +80 -48
- package/dist/server-manager/core.js +183 -0
- package/dist/server-manager/index.js +81 -0
- package/dist/server-manager/operations.js +208 -0
- package/dist/services/build-manager.js +2 -69
- package/dist/services/index.js +21 -48
- package/dist/services/screenshot-server.js +40 -74
- package/dist/services/server-manager.js +45 -80
- package/dist/services/static-report-generator.js +21 -163
- package/dist/services/test-runner.js +90 -249
- package/dist/services/uploader.js +56 -358
- package/dist/tdd/core/hotspot-coverage.js +112 -0
- package/dist/tdd/core/signature.js +101 -0
- package/dist/tdd/index.js +19 -0
- package/dist/tdd/metadata/baseline-metadata.js +103 -0
- package/dist/tdd/metadata/hotspot-metadata.js +93 -0
- package/dist/tdd/services/baseline-downloader.js +151 -0
- package/dist/tdd/services/baseline-manager.js +166 -0
- package/dist/tdd/services/comparison-service.js +230 -0
- package/dist/tdd/services/hotspot-service.js +71 -0
- package/dist/tdd/services/result-service.js +123 -0
- package/dist/tdd/tdd-service.js +1081 -0
- package/dist/test-runner/core.js +255 -0
- package/dist/test-runner/index.js +13 -0
- package/dist/test-runner/operations.js +483 -0
- package/dist/types/client.d.ts +4 -2
- package/dist/types/index.d.ts +5 -0
- package/dist/uploader/core.js +396 -0
- package/dist/uploader/index.js +11 -0
- package/dist/uploader/operations.js +412 -0
- package/dist/utils/config-schema.js +8 -3
- package/package.json +7 -12
- package/dist/services/api-service.js +0 -412
- package/dist/services/auth-service.js +0 -226
- package/dist/services/config-service.js +0 -369
- package/dist/services/html-report-generator.js +0 -455
- package/dist/services/project-service.js +0 -326
- package/dist/services/report-generator/report.css +0 -411
- package/dist/services/report-generator/viewer.js +0 -102
- package/dist/services/tdd-service.js +0 -1429
package/dist/commands/run.js
CHANGED
|
@@ -1,65 +1,118 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Run command implementation
|
|
3
|
+
* Uses functional operations directly - no class wrappers needed
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { spawn as defaultSpawn } from 'node:child_process';
|
|
7
|
+
import { createBuild as defaultCreateApiBuild, createApiClient as defaultCreateApiClient, finalizeBuild as defaultFinalizeApiBuild, getBuild as defaultGetBuild } from '../api/index.js';
|
|
8
|
+
import { VizzlyError } from '../errors/vizzly-error.js';
|
|
9
|
+
import { createServerManager as defaultCreateServerManager } from '../server-manager/index.js';
|
|
10
|
+
import { createBuildObject as defaultCreateBuildObject } from '../services/build-manager.js';
|
|
11
|
+
import { createUploader as defaultCreateUploader } from '../services/uploader.js';
|
|
12
|
+
import { finalizeBuild as defaultFinalizeBuild, runTests as defaultRunTests } from '../test-runner/index.js';
|
|
13
|
+
import { loadConfig as defaultLoadConfig } from '../utils/config-loader.js';
|
|
14
|
+
import { detectBranch as defaultDetectBranch, detectCommit as defaultDetectCommit, detectCommitMessage as defaultDetectCommitMessage, detectPullRequestNumber as defaultDetectPullRequestNumber, generateBuildNameWithGit as defaultGenerateBuildNameWithGit } from '../utils/git.js';
|
|
15
|
+
import * as defaultOutput from '../utils/output.js';
|
|
5
16
|
|
|
6
17
|
/**
|
|
7
18
|
* Run command implementation
|
|
8
19
|
* @param {string} testCommand - Test command to execute
|
|
9
20
|
* @param {Object} options - Command options
|
|
10
21
|
* @param {Object} globalOptions - Global CLI options
|
|
22
|
+
* @param {Object} deps - Dependencies for testing
|
|
11
23
|
*/
|
|
12
|
-
export async function runCommand(testCommand, options = {}, globalOptions = {}) {
|
|
24
|
+
export async function runCommand(testCommand, options = {}, globalOptions = {}, deps = {}) {
|
|
25
|
+
let {
|
|
26
|
+
loadConfig = defaultLoadConfig,
|
|
27
|
+
createApiClient = defaultCreateApiClient,
|
|
28
|
+
createApiBuild = defaultCreateApiBuild,
|
|
29
|
+
finalizeApiBuild = defaultFinalizeApiBuild,
|
|
30
|
+
getBuild = defaultGetBuild,
|
|
31
|
+
createServerManager = defaultCreateServerManager,
|
|
32
|
+
createBuildObject = defaultCreateBuildObject,
|
|
33
|
+
createUploader = defaultCreateUploader,
|
|
34
|
+
finalizeBuild = defaultFinalizeBuild,
|
|
35
|
+
runTests = defaultRunTests,
|
|
36
|
+
detectBranch = defaultDetectBranch,
|
|
37
|
+
detectCommit = defaultDetectCommit,
|
|
38
|
+
detectCommitMessage = defaultDetectCommitMessage,
|
|
39
|
+
detectPullRequestNumber = defaultDetectPullRequestNumber,
|
|
40
|
+
generateBuildNameWithGit = defaultGenerateBuildNameWithGit,
|
|
41
|
+
spawn = defaultSpawn,
|
|
42
|
+
output = defaultOutput,
|
|
43
|
+
exit = code => process.exit(code),
|
|
44
|
+
processOn = (event, handler) => process.on(event, handler),
|
|
45
|
+
processRemoveListener = (event, handler) => process.removeListener(event, handler)
|
|
46
|
+
} = deps;
|
|
13
47
|
output.configure({
|
|
14
48
|
json: globalOptions.json,
|
|
15
49
|
verbose: globalOptions.verbose,
|
|
16
50
|
color: !globalOptions.noColor
|
|
17
51
|
});
|
|
18
|
-
let
|
|
52
|
+
let serverManager = null;
|
|
53
|
+
let testProcess = null;
|
|
19
54
|
let buildId = null;
|
|
20
55
|
let startTime = null;
|
|
21
56
|
let isTddMode = false;
|
|
57
|
+
let config = null;
|
|
22
58
|
|
|
23
59
|
// Ensure cleanup on exit
|
|
24
|
-
|
|
60
|
+
let cleanup = async () => {
|
|
25
61
|
output.cleanup();
|
|
26
62
|
|
|
27
|
-
//
|
|
28
|
-
if (
|
|
63
|
+
// Kill test process if running
|
|
64
|
+
if (testProcess && !testProcess.killed) {
|
|
65
|
+
testProcess.kill('SIGKILL');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Stop server
|
|
69
|
+
if (serverManager) {
|
|
29
70
|
try {
|
|
30
|
-
await
|
|
71
|
+
await serverManager.stop();
|
|
31
72
|
} catch {
|
|
32
73
|
// Silent fail
|
|
33
74
|
}
|
|
34
75
|
}
|
|
35
76
|
|
|
36
77
|
// Finalize build if we have one
|
|
37
|
-
if (
|
|
78
|
+
if (buildId && config) {
|
|
38
79
|
try {
|
|
39
|
-
|
|
40
|
-
await
|
|
80
|
+
let executionTime = Date.now() - (startTime || Date.now());
|
|
81
|
+
await finalizeBuild({
|
|
82
|
+
buildId,
|
|
83
|
+
tdd: isTddMode,
|
|
84
|
+
success: false,
|
|
85
|
+
executionTime,
|
|
86
|
+
config,
|
|
87
|
+
deps: {
|
|
88
|
+
serverManager,
|
|
89
|
+
createApiClient,
|
|
90
|
+
finalizeApiBuild,
|
|
91
|
+
output
|
|
92
|
+
}
|
|
93
|
+
});
|
|
41
94
|
} catch {
|
|
42
95
|
// Silent fail on cleanup
|
|
43
96
|
}
|
|
44
97
|
}
|
|
45
98
|
};
|
|
46
|
-
|
|
99
|
+
let sigintHandler = async () => {
|
|
47
100
|
await cleanup();
|
|
48
|
-
|
|
101
|
+
exit(1);
|
|
49
102
|
};
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
103
|
+
let exitHandler = () => output.cleanup();
|
|
104
|
+
processOn('SIGINT', sigintHandler);
|
|
105
|
+
processOn('exit', exitHandler);
|
|
53
106
|
try {
|
|
54
107
|
// Load configuration with CLI overrides
|
|
55
|
-
|
|
108
|
+
let allOptions = {
|
|
56
109
|
...globalOptions,
|
|
57
110
|
...options
|
|
58
111
|
};
|
|
59
112
|
output.debug('[RUN] Loading config', {
|
|
60
113
|
hasToken: !!allOptions.token
|
|
61
114
|
});
|
|
62
|
-
|
|
115
|
+
config = await loadConfig(globalOptions.config, allOptions);
|
|
63
116
|
output.debug('[RUN] Config loaded', {
|
|
64
117
|
hasApiKey: !!config.apiKey,
|
|
65
118
|
apiKeyPrefix: config.apiKey ? `${config.apiKey.substring(0, 8)}***` : 'NONE'
|
|
@@ -78,15 +131,19 @@ export async function runCommand(testCommand, options = {}, globalOptions = {})
|
|
|
78
131
|
// Validate API token (unless --allow-no-token is set)
|
|
79
132
|
if (!config.apiKey && !config.allowNoToken) {
|
|
80
133
|
output.error('API token required. Use --token, set VIZZLY_TOKEN environment variable, or use --allow-no-token to run without uploading');
|
|
81
|
-
|
|
134
|
+
exit(1);
|
|
135
|
+
return {
|
|
136
|
+
success: false,
|
|
137
|
+
reason: 'no-api-key'
|
|
138
|
+
};
|
|
82
139
|
}
|
|
83
140
|
|
|
84
141
|
// Collect git metadata and build info
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
142
|
+
let branch = await detectBranch(options.branch);
|
|
143
|
+
let commit = await detectCommit(options.commit);
|
|
144
|
+
let message = options.message || (await detectCommitMessage());
|
|
145
|
+
let buildName = await generateBuildNameWithGit(options.buildName);
|
|
146
|
+
let pullRequestNumber = detectPullRequestNumber();
|
|
90
147
|
if (globalOptions.verbose) {
|
|
91
148
|
output.info('Configuration loaded');
|
|
92
149
|
output.debug('Config details', {
|
|
@@ -102,9 +159,9 @@ export async function runCommand(testCommand, options = {}, globalOptions = {})
|
|
|
102
159
|
});
|
|
103
160
|
}
|
|
104
161
|
|
|
105
|
-
// Create
|
|
162
|
+
// Create functional dependencies
|
|
106
163
|
output.startSpinner('Initializing test runner...');
|
|
107
|
-
|
|
164
|
+
let configWithVerbose = {
|
|
108
165
|
...config,
|
|
109
166
|
verbose: globalOptions.verbose,
|
|
110
167
|
uploadAll: options.uploadAll || false
|
|
@@ -112,59 +169,29 @@ export async function runCommand(testCommand, options = {}, globalOptions = {})
|
|
|
112
169
|
output.debug('[RUN] Creating services', {
|
|
113
170
|
hasApiKey: !!configWithVerbose.apiKey
|
|
114
171
|
});
|
|
115
|
-
const services = createServices(configWithVerbose, 'run');
|
|
116
|
-
testRunner = services.testRunner;
|
|
117
|
-
output.stopSpinner();
|
|
118
172
|
|
|
119
|
-
//
|
|
120
|
-
|
|
173
|
+
// Create server manager (functional object)
|
|
174
|
+
serverManager = createServerManager(configWithVerbose, {});
|
|
121
175
|
|
|
122
|
-
//
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
} = progressData;
|
|
127
|
-
output.progress(progressMessage || 'Running tests...');
|
|
128
|
-
});
|
|
129
|
-
testRunner.on('test-output', data => {
|
|
130
|
-
// In non-JSON mode, show test output directly
|
|
131
|
-
if (!globalOptions.json) {
|
|
132
|
-
output.stopSpinner();
|
|
133
|
-
output.print(data.data);
|
|
134
|
-
}
|
|
135
|
-
});
|
|
136
|
-
testRunner.on('server-ready', serverInfo => {
|
|
137
|
-
if (globalOptions.verbose) {
|
|
138
|
-
output.info(`Screenshot server running on port ${serverInfo.port}`);
|
|
139
|
-
output.debug('Server details', serverInfo);
|
|
140
|
-
}
|
|
141
|
-
});
|
|
142
|
-
testRunner.on('screenshot-captured', screenshotInfo => {
|
|
143
|
-
output.info(`Vizzly: Screenshot captured - ${screenshotInfo.name}`);
|
|
144
|
-
});
|
|
145
|
-
testRunner.on('build-created', buildInfo => {
|
|
146
|
-
buildUrl = buildInfo.url;
|
|
147
|
-
buildId = buildInfo.buildId;
|
|
148
|
-
if (globalOptions.verbose) {
|
|
149
|
-
output.info(`Build created: ${buildInfo.buildId} - ${buildInfo.name}`);
|
|
176
|
+
// Create build manager (functional object)
|
|
177
|
+
let buildManager = {
|
|
178
|
+
async createBuild(buildOptions) {
|
|
179
|
+
return createBuildObject(buildOptions);
|
|
150
180
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
});
|
|
158
|
-
testRunner.on('error', error => {
|
|
159
|
-
output.stopSpinner();
|
|
160
|
-
output.error('Test runner error occurred', error);
|
|
161
|
-
});
|
|
162
|
-
testRunner.on('build-finalize-failed', errorInfo => {
|
|
163
|
-
output.warn(`Failed to finalize build ${errorInfo.buildId}: ${errorInfo.error}`);
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
// Create uploader for --wait functionality
|
|
184
|
+
let uploader = createUploader({
|
|
185
|
+
...configWithVerbose,
|
|
186
|
+
command: 'run'
|
|
164
187
|
});
|
|
188
|
+
output.stopSpinner();
|
|
189
|
+
|
|
190
|
+
// Track build URL for display
|
|
191
|
+
let buildUrl = null;
|
|
165
192
|
|
|
166
193
|
// Prepare run options
|
|
167
|
-
|
|
194
|
+
let runOptions = {
|
|
168
195
|
testCommand,
|
|
169
196
|
port: config.server.port,
|
|
170
197
|
timeout: config.server.timeout,
|
|
@@ -188,7 +215,43 @@ export async function runCommand(testCommand, options = {}, globalOptions = {})
|
|
|
188
215
|
isTddMode = runOptions.tdd || false;
|
|
189
216
|
let result;
|
|
190
217
|
try {
|
|
191
|
-
result = await
|
|
218
|
+
result = await runTests({
|
|
219
|
+
runOptions,
|
|
220
|
+
config: configWithVerbose,
|
|
221
|
+
deps: {
|
|
222
|
+
serverManager,
|
|
223
|
+
buildManager,
|
|
224
|
+
spawn: (command, spawnOptions) => {
|
|
225
|
+
let proc = spawn(command, spawnOptions);
|
|
226
|
+
testProcess = proc;
|
|
227
|
+
return proc;
|
|
228
|
+
},
|
|
229
|
+
createApiClient,
|
|
230
|
+
createApiBuild,
|
|
231
|
+
getBuild,
|
|
232
|
+
finalizeApiBuild,
|
|
233
|
+
createError: (msg, code) => new VizzlyError(msg, code),
|
|
234
|
+
output,
|
|
235
|
+
onBuildCreated: data => {
|
|
236
|
+
buildUrl = data.url;
|
|
237
|
+
buildId = data.buildId;
|
|
238
|
+
if (globalOptions.verbose) {
|
|
239
|
+
output.info(`Build created: ${data.buildId}`);
|
|
240
|
+
}
|
|
241
|
+
if (buildUrl) {
|
|
242
|
+
output.info(`Vizzly: ${buildUrl}`);
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
onServerReady: data => {
|
|
246
|
+
if (globalOptions.verbose) {
|
|
247
|
+
output.info(`Screenshot server running on port ${data.port}`);
|
|
248
|
+
}
|
|
249
|
+
},
|
|
250
|
+
onFinalizeFailed: data => {
|
|
251
|
+
output.warn(`Failed to finalize build ${data.buildId}: ${data.error}`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
});
|
|
192
255
|
|
|
193
256
|
// Store buildId for cleanup purposes
|
|
194
257
|
if (result.buildId) {
|
|
@@ -210,8 +273,8 @@ export async function runCommand(testCommand, options = {}, globalOptions = {})
|
|
|
210
273
|
// Check if it's a test command failure (as opposed to setup failure)
|
|
211
274
|
if (error.code === 'TEST_COMMAND_FAILED' || error.code === 'TEST_COMMAND_INTERRUPTED') {
|
|
212
275
|
// Extract exit code from error message if available
|
|
213
|
-
|
|
214
|
-
|
|
276
|
+
let exitCodeMatch = error.message.match(/exited with code (\d+)/);
|
|
277
|
+
let exitCode = exitCodeMatch ? parseInt(exitCodeMatch[1], 10) : 1;
|
|
215
278
|
output.error('Test run failed');
|
|
216
279
|
return {
|
|
217
280
|
success: false,
|
|
@@ -233,10 +296,7 @@ export async function runCommand(testCommand, options = {}, globalOptions = {})
|
|
|
233
296
|
if (runOptions.wait) {
|
|
234
297
|
output.info('Waiting for build completion...');
|
|
235
298
|
output.startSpinner('Processing comparisons...');
|
|
236
|
-
|
|
237
|
-
uploader
|
|
238
|
-
} = services;
|
|
239
|
-
const buildResult = await uploader.waitForBuild(result.buildId);
|
|
299
|
+
let buildResult = await uploader.waitForBuild(result.buildId);
|
|
240
300
|
output.success('Build processing completed');
|
|
241
301
|
|
|
242
302
|
// Exit with appropriate code based on comparison results
|
|
@@ -250,6 +310,10 @@ export async function runCommand(testCommand, options = {}, globalOptions = {})
|
|
|
250
310
|
}
|
|
251
311
|
}
|
|
252
312
|
output.cleanup();
|
|
313
|
+
return {
|
|
314
|
+
success: true,
|
|
315
|
+
result
|
|
316
|
+
};
|
|
253
317
|
} catch (error) {
|
|
254
318
|
output.stopSpinner();
|
|
255
319
|
|
|
@@ -263,11 +327,15 @@ export async function runCommand(testCommand, options = {}, globalOptions = {})
|
|
|
263
327
|
errorContext = 'Server startup failed';
|
|
264
328
|
}
|
|
265
329
|
output.error(errorContext, error);
|
|
266
|
-
|
|
330
|
+
exit(1);
|
|
331
|
+
return {
|
|
332
|
+
success: false,
|
|
333
|
+
error
|
|
334
|
+
};
|
|
267
335
|
} finally {
|
|
268
336
|
// Remove event listeners to prevent memory leaks
|
|
269
|
-
|
|
270
|
-
|
|
337
|
+
processRemoveListener('SIGINT', sigintHandler);
|
|
338
|
+
processRemoveListener('exit', exitHandler);
|
|
271
339
|
}
|
|
272
340
|
}
|
|
273
341
|
|
|
@@ -277,30 +345,30 @@ export async function runCommand(testCommand, options = {}, globalOptions = {})
|
|
|
277
345
|
* @param {Object} options - Command options
|
|
278
346
|
*/
|
|
279
347
|
export function validateRunOptions(testCommand, options) {
|
|
280
|
-
|
|
348
|
+
let errors = [];
|
|
281
349
|
if (!testCommand || testCommand.trim() === '') {
|
|
282
350
|
errors.push('Test command is required');
|
|
283
351
|
}
|
|
284
352
|
if (options.port) {
|
|
285
|
-
|
|
353
|
+
let port = parseInt(options.port, 10);
|
|
286
354
|
if (Number.isNaN(port) || port < 1 || port > 65535) {
|
|
287
355
|
errors.push('Port must be a valid number between 1 and 65535');
|
|
288
356
|
}
|
|
289
357
|
}
|
|
290
358
|
if (options.timeout) {
|
|
291
|
-
|
|
359
|
+
let timeout = parseInt(options.timeout, 10);
|
|
292
360
|
if (Number.isNaN(timeout) || timeout < 1000) {
|
|
293
361
|
errors.push('Timeout must be at least 1000 milliseconds');
|
|
294
362
|
}
|
|
295
363
|
}
|
|
296
364
|
if (options.batchSize !== undefined) {
|
|
297
|
-
|
|
365
|
+
let n = parseInt(options.batchSize, 10);
|
|
298
366
|
if (!Number.isFinite(n) || n <= 0) {
|
|
299
367
|
errors.push('Batch size must be a positive integer');
|
|
300
368
|
}
|
|
301
369
|
}
|
|
302
370
|
if (options.uploadTimeout !== undefined) {
|
|
303
|
-
|
|
371
|
+
let n = parseInt(options.uploadTimeout, 10);
|
|
304
372
|
if (!Number.isFinite(n) || n <= 0) {
|
|
305
373
|
errors.push('Upload timeout must be a positive integer (milliseconds)');
|
|
306
374
|
}
|
package/dist/commands/status.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Status command implementation
|
|
3
|
+
* Uses functional API operations directly
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { createApiClient, getBuild } from '../api/index.js';
|
|
2
7
|
import { loadConfig } from '../utils/config-loader.js';
|
|
3
8
|
import { getApiUrl } from '../utils/environment-config.js';
|
|
4
9
|
import * as output from '../utils/output.js';
|
|
@@ -19,11 +24,11 @@ export async function statusCommand(buildId, options = {}, globalOptions = {}) {
|
|
|
19
24
|
output.info(`Checking status for build: ${buildId}`);
|
|
20
25
|
|
|
21
26
|
// Load configuration with CLI overrides
|
|
22
|
-
|
|
27
|
+
let allOptions = {
|
|
23
28
|
...globalOptions,
|
|
24
29
|
...options
|
|
25
30
|
};
|
|
26
|
-
|
|
31
|
+
let config = await loadConfig(globalOptions.config, allOptions);
|
|
27
32
|
|
|
28
33
|
// Validate API token
|
|
29
34
|
if (!config.apiKey) {
|
|
@@ -31,19 +36,18 @@ export async function statusCommand(buildId, options = {}, globalOptions = {}) {
|
|
|
31
36
|
process.exit(1);
|
|
32
37
|
}
|
|
33
38
|
|
|
34
|
-
// Get API
|
|
39
|
+
// Get build details via functional API
|
|
35
40
|
output.startSpinner('Fetching build status...');
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const buildStatus = await apiService.getBuild(buildId);
|
|
41
|
+
let client = createApiClient({
|
|
42
|
+
baseUrl: config.apiUrl,
|
|
43
|
+
token: config.apiKey,
|
|
44
|
+
command: 'status'
|
|
45
|
+
});
|
|
46
|
+
let buildStatus = await getBuild(client, buildId);
|
|
43
47
|
output.stopSpinner();
|
|
44
48
|
|
|
45
49
|
// Extract build data from API response
|
|
46
|
-
|
|
50
|
+
let build = buildStatus.build || buildStatus;
|
|
47
51
|
|
|
48
52
|
// Display build summary
|
|
49
53
|
output.success(`Build: ${build.name || build.id}`);
|
|
@@ -77,15 +81,15 @@ export async function statusCommand(buildId, options = {}, globalOptions = {}) {
|
|
|
77
81
|
}
|
|
78
82
|
|
|
79
83
|
// Show build URL if we can construct it
|
|
80
|
-
|
|
84
|
+
let baseUrl = config.baseUrl || getApiUrl();
|
|
81
85
|
if (baseUrl && build.project_id) {
|
|
82
|
-
|
|
86
|
+
let buildUrl = baseUrl.replace('/api', '') + `/projects/${build.project_id}/builds/${build.id}`;
|
|
83
87
|
output.info(`View Build: ${buildUrl}`);
|
|
84
88
|
}
|
|
85
89
|
|
|
86
90
|
// Output JSON data for --json mode
|
|
87
91
|
if (globalOptions.json) {
|
|
88
|
-
|
|
92
|
+
let statusData = {
|
|
89
93
|
buildId: build.id,
|
|
90
94
|
status: build.status,
|
|
91
95
|
name: build.name,
|
|
@@ -131,9 +135,9 @@ export async function statusCommand(buildId, options = {}, globalOptions = {}) {
|
|
|
131
135
|
|
|
132
136
|
// Show progress if build is still processing
|
|
133
137
|
if (build.status === 'processing' || build.status === 'pending') {
|
|
134
|
-
|
|
138
|
+
let totalJobs = build.completed_jobs + build.failed_jobs + build.processing_screenshots;
|
|
135
139
|
if (totalJobs > 0) {
|
|
136
|
-
|
|
140
|
+
let progress = (build.completed_jobs + build.failed_jobs) / totalJobs;
|
|
137
141
|
output.info(`Progress: ${Math.round(progress * 100)}% complete`);
|
|
138
142
|
}
|
|
139
143
|
}
|
|
@@ -155,7 +159,7 @@ export async function statusCommand(buildId, options = {}, globalOptions = {}) {
|
|
|
155
159
|
* @param {Object} options - Command options
|
|
156
160
|
*/
|
|
157
161
|
export function validateStatusOptions(buildId) {
|
|
158
|
-
|
|
162
|
+
let errors = [];
|
|
159
163
|
if (!buildId || buildId.trim() === '') {
|
|
160
164
|
errors.push('Build ID is required');
|
|
161
165
|
}
|