@argos-ci/cli 4.1.5-alpha.2 → 4.2.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/index.mjs +312 -21
- package/package.json +9 -5
package/dist/index.mjs
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import { readFile } from "node:fs/promises";
|
|
1
|
+
import { mkdir, readFile, rename, unlink, writeFile } from "node:fs/promises";
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
4
|
import { Option, program } from "commander";
|
|
5
5
|
import { finalize, skip, upload } from "@argos-ci/core";
|
|
6
6
|
import ora from "ora";
|
|
7
|
-
import { createClient, throwAPIError } from "@argos-ci/api-client";
|
|
7
|
+
import { createClient, formatAPIError, throwAPIError } from "@argos-ci/api-client";
|
|
8
|
+
import { homedir } from "node:os";
|
|
9
|
+
import { createServer } from "node:http";
|
|
10
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
11
|
+
import open from "open";
|
|
8
12
|
//#region src/options.ts
|
|
9
13
|
const parallelNonceOption = new Option("--parallel-nonce <string>", "A unique ID for this parallel build").env("ARGOS_PARALLEL_NONCE");
|
|
10
14
|
const tokenOption = new Option("--token <token>", "Repository token").env("ARGOS_TOKEN");
|
|
@@ -92,11 +96,77 @@ function skipCommand(program) {
|
|
|
92
96
|
});
|
|
93
97
|
}
|
|
94
98
|
//#endregion
|
|
99
|
+
//#region src/auth.ts
|
|
100
|
+
function getConfigPaths() {
|
|
101
|
+
const configDir = resolve(homedir(), ".config", "argos-ci");
|
|
102
|
+
return {
|
|
103
|
+
configDir,
|
|
104
|
+
configPath: resolve(configDir, "config.json")
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function parseConfig(raw) {
|
|
108
|
+
let parsed;
|
|
109
|
+
try {
|
|
110
|
+
parsed = JSON.parse(raw);
|
|
111
|
+
} catch {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return null;
|
|
115
|
+
const record = parsed;
|
|
116
|
+
if (!("token" in record) || record.token === void 0) return {};
|
|
117
|
+
if (typeof record.token !== "string") return null;
|
|
118
|
+
return { token: record.token };
|
|
119
|
+
}
|
|
120
|
+
async function clearConfig() {
|
|
121
|
+
const { configPath } = getConfigPaths();
|
|
122
|
+
try {
|
|
123
|
+
await unlink(configPath);
|
|
124
|
+
} catch {}
|
|
125
|
+
}
|
|
126
|
+
async function readConfig() {
|
|
127
|
+
const { configPath } = getConfigPaths();
|
|
128
|
+
try {
|
|
129
|
+
const config = parseConfig(await readFile(configPath, "utf8"));
|
|
130
|
+
if (config === null) {
|
|
131
|
+
console.warn("Warning: Config file is invalid and has been cleared. Run `argos login` again.");
|
|
132
|
+
await clearConfig();
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
return config;
|
|
136
|
+
} catch (err) {
|
|
137
|
+
if (err instanceof Error && err.code === "ENOENT") return null;
|
|
138
|
+
throw err;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
async function writeConfig(config) {
|
|
142
|
+
const { configDir, configPath } = getConfigPaths();
|
|
143
|
+
await mkdir(configDir, { recursive: true });
|
|
144
|
+
const tmpPath = configPath + ".tmp";
|
|
145
|
+
await writeFile(tmpPath, JSON.stringify(config, null, 2), { mode: 384 });
|
|
146
|
+
await rename(tmpPath, configPath);
|
|
147
|
+
}
|
|
148
|
+
async function getStoredToken() {
|
|
149
|
+
return (await readConfig())?.token;
|
|
150
|
+
}
|
|
151
|
+
async function saveToken(token) {
|
|
152
|
+
await writeConfig({
|
|
153
|
+
...await readConfig() ?? {},
|
|
154
|
+
token
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
async function removeToken() {
|
|
158
|
+
await writeConfig({});
|
|
159
|
+
}
|
|
160
|
+
//#endregion
|
|
95
161
|
//#region src/commands/build.ts
|
|
96
|
-
|
|
97
|
-
|
|
162
|
+
const reviewConclusions = {
|
|
163
|
+
approve: "APPROVE",
|
|
164
|
+
"request-changes": "REQUEST_CHANGES"
|
|
165
|
+
};
|
|
166
|
+
async function getTokenOrExit(options) {
|
|
167
|
+
const token = options.token || process.env["ARGOS_TOKEN"] || await getStoredToken();
|
|
98
168
|
if (!token) {
|
|
99
|
-
console.error("Error: No Argos token found. Use --token or
|
|
169
|
+
console.error("Error: No Argos token found. Use --token, set ARGOS_TOKEN, or run `argos login`.");
|
|
100
170
|
process.exit(1);
|
|
101
171
|
}
|
|
102
172
|
return token;
|
|
@@ -104,20 +174,26 @@ function getToken(options) {
|
|
|
104
174
|
function getAPIBaseURL() {
|
|
105
175
|
return process.env["ARGOS_API_BASE_URL"];
|
|
106
176
|
}
|
|
107
|
-
function
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
177
|
+
function getProjectTokenOrExit(options) {
|
|
178
|
+
const projectToken = options.token || process.env["ARGOS_TOKEN"];
|
|
179
|
+
if (!projectToken) {
|
|
180
|
+
console.error("Error: No Argos project token found. Use --token or set ARGOS_TOKEN.");
|
|
181
|
+
process.exit(1);
|
|
182
|
+
}
|
|
183
|
+
return projectToken;
|
|
112
184
|
}
|
|
113
185
|
function isBuildPending(build) {
|
|
114
186
|
return build.status === "pending" || build.status === "progress";
|
|
115
187
|
}
|
|
116
|
-
function
|
|
188
|
+
function parseBuildReferenceDetailsOrExit(buildReference) {
|
|
117
189
|
const parsedBuildNumber = Number(buildReference);
|
|
118
|
-
if (Number.isFinite(parsedBuildNumber) && Number.isInteger(parsedBuildNumber)) return parsedBuildNumber;
|
|
119
|
-
const urlMatch = buildReference.match(/^https:\/\/app\.argos-ci\.(?:com|dev(?::\d+)?)
|
|
120
|
-
if (urlMatch) return
|
|
190
|
+
if (Number.isFinite(parsedBuildNumber) && Number.isInteger(parsedBuildNumber)) return { buildNumber: parsedBuildNumber };
|
|
191
|
+
const urlMatch = buildReference.match(/^https:\/\/app\.argos-ci\.(?:com|dev(?::\d+)?)\/(?<owner>[^/?#]+)\/(?<project>[^/?#]+)\/builds\/(?<buildNumber>\d+)(?:\/?$|[?#])/);
|
|
192
|
+
if (urlMatch?.groups) return {
|
|
193
|
+
owner: urlMatch.groups.owner,
|
|
194
|
+
project: urlMatch.groups.project,
|
|
195
|
+
buildNumber: Number(urlMatch.groups.buildNumber)
|
|
196
|
+
};
|
|
121
197
|
console.error(`Error: Build reference must be a valid build number or Argos build URL (https://app.argos-ci.com/.../builds/<number>), got "${buildReference}".`);
|
|
122
198
|
process.exit(1);
|
|
123
199
|
}
|
|
@@ -191,6 +267,20 @@ function printSnapshots(diffs, build) {
|
|
|
191
267
|
];
|
|
192
268
|
console.log(lines.slice(0, -1).join("\n"));
|
|
193
269
|
}
|
|
270
|
+
function printReview(review, buildNumber) {
|
|
271
|
+
const lines = [
|
|
272
|
+
`Review #${review.id}`,
|
|
273
|
+
`State: ${review.state}`,
|
|
274
|
+
`Build: #${buildNumber}`
|
|
275
|
+
];
|
|
276
|
+
console.log(lines.join("\n"));
|
|
277
|
+
}
|
|
278
|
+
function createProjectClient(options) {
|
|
279
|
+
return createClient({
|
|
280
|
+
authToken: getProjectTokenOrExit(options),
|
|
281
|
+
baseUrl: getAPIBaseURL()
|
|
282
|
+
});
|
|
283
|
+
}
|
|
194
284
|
async function fetchAllDiffs(client, project, buildNumber, options) {
|
|
195
285
|
const results = [];
|
|
196
286
|
let page = 1;
|
|
@@ -205,7 +295,7 @@ async function fetchAllDiffs(client, project, buildNumber, options) {
|
|
|
205
295
|
path: {
|
|
206
296
|
owner: project.account.slug,
|
|
207
297
|
project: project.name,
|
|
208
|
-
buildNumber
|
|
298
|
+
buildNumber: String(buildNumber)
|
|
209
299
|
},
|
|
210
300
|
query
|
|
211
301
|
} });
|
|
@@ -232,7 +322,7 @@ async function fetchBuildByNumber(client, project, buildNumber, errorLabel) {
|
|
|
232
322
|
const { data, error, response } = await client.GET("/projects/{owner}/{project}/builds/{buildNumber}", { params: { path: {
|
|
233
323
|
owner: project.account.slug,
|
|
234
324
|
project: project.name,
|
|
235
|
-
buildNumber
|
|
325
|
+
buildNumber: String(buildNumber)
|
|
236
326
|
} } });
|
|
237
327
|
if (error) {
|
|
238
328
|
if (response.status === 404) {
|
|
@@ -251,8 +341,8 @@ function buildCommand(program) {
|
|
|
251
341
|
const build = program.command("build").description("Manage Argos builds");
|
|
252
342
|
const createJsonOption = () => new Option("--json", "Output machine-readable JSON instead of human-readable text");
|
|
253
343
|
build.command("get").description("Fetch build metadata").argument("<buildReference>", "Build number or Argos build URL").addOption(tokenOption).addOption(createJsonOption()).action(async (buildReference, options) => {
|
|
254
|
-
const buildNumber =
|
|
255
|
-
const client =
|
|
344
|
+
const { buildNumber } = parseBuildReferenceDetailsOrExit(buildReference);
|
|
345
|
+
const client = createProjectClient(options);
|
|
256
346
|
const build = await fetchBuildByNumber(client, await fetchProject(client), buildNumber, buildReference);
|
|
257
347
|
if (options.json) {
|
|
258
348
|
console.log(JSON.stringify(build, null, 2));
|
|
@@ -261,8 +351,8 @@ function buildCommand(program) {
|
|
|
261
351
|
printBuild(build);
|
|
262
352
|
});
|
|
263
353
|
build.command("snapshots").description("Fetch snapshot diffs for a build").argument("<buildReference>", "Build number or Argos build URL").option("--needs-review", "Only include snapshot diffs that require review").addOption(tokenOption).addOption(createJsonOption()).action(async (buildReference, options) => {
|
|
264
|
-
const buildNumber =
|
|
265
|
-
const client =
|
|
354
|
+
const { buildNumber } = parseBuildReferenceDetailsOrExit(buildReference);
|
|
355
|
+
const client = createProjectClient(options);
|
|
266
356
|
const project = await fetchProject(client);
|
|
267
357
|
const build = await fetchBuildByNumber(client, project, buildNumber, buildReference);
|
|
268
358
|
if (isBuildPending(build)) {
|
|
@@ -273,13 +363,213 @@ function buildCommand(program) {
|
|
|
273
363
|
console.log(`Build #${build.number} is still processing (${build.status}). Try again in a moment.`);
|
|
274
364
|
return;
|
|
275
365
|
}
|
|
276
|
-
const diffs = await fetchAllDiffs(client, project,
|
|
366
|
+
const diffs = await fetchAllDiffs(client, project, buildNumber, { needsReview: Boolean(options.needsReview) });
|
|
277
367
|
if (options.json) {
|
|
278
368
|
console.log(JSON.stringify(diffs, null, 2));
|
|
279
369
|
return;
|
|
280
370
|
}
|
|
281
371
|
printSnapshots(diffs, build);
|
|
282
372
|
});
|
|
373
|
+
build.command("review").description("Create a review on a build").argument("<buildReference>", "Build number or Argos build URL").addOption(new Option("--conclusion <conclusion>", "Overall review conclusion for the build: \"approve\" or \"request-changes\"").choices(Object.keys(reviewConclusions)).makeOptionMandatory()).option("--project <project>", "Project path in owner/project format").addOption(tokenOption).addOption(createJsonOption()).action(async (buildReference, options) => {
|
|
374
|
+
const conclusion = reviewConclusions[options.conclusion];
|
|
375
|
+
const buildReferenceDetails = parseBuildReferenceDetailsOrExit(buildReference);
|
|
376
|
+
let owner;
|
|
377
|
+
let project;
|
|
378
|
+
if (buildReferenceDetails.owner && buildReferenceDetails.project) {
|
|
379
|
+
owner = buildReferenceDetails.owner;
|
|
380
|
+
project = buildReferenceDetails.project;
|
|
381
|
+
} else if (options.project) {
|
|
382
|
+
const parts = options.project.split("/");
|
|
383
|
+
if (parts.length !== 2 || !parts[0] || !parts[1]) {
|
|
384
|
+
console.error("Error: --project must be in the format 'owner/project'.");
|
|
385
|
+
process.exit(1);
|
|
386
|
+
}
|
|
387
|
+
[owner, project] = parts;
|
|
388
|
+
} else {
|
|
389
|
+
console.error("Error: --project <owner/project> is required for build-number references.");
|
|
390
|
+
process.exit(1);
|
|
391
|
+
}
|
|
392
|
+
const { data, error } = await createClient({
|
|
393
|
+
authToken: await getTokenOrExit(options),
|
|
394
|
+
baseUrl: getAPIBaseURL()
|
|
395
|
+
}).POST("/projects/{owner}/{project}/builds/{buildNumber}/reviews", {
|
|
396
|
+
params: { path: {
|
|
397
|
+
owner,
|
|
398
|
+
project,
|
|
399
|
+
buildNumber: String(buildReferenceDetails.buildNumber)
|
|
400
|
+
} },
|
|
401
|
+
body: { conclusion }
|
|
402
|
+
});
|
|
403
|
+
if (error) {
|
|
404
|
+
const message = formatAPIError(error);
|
|
405
|
+
throw new Error([
|
|
406
|
+
`Failed to create review for ${owner}/${project} build #${buildReferenceDetails.buildNumber}: ${message}`,
|
|
407
|
+
"Make sure the token passed with --token is a user access token with access to this project.",
|
|
408
|
+
"Project tokens from ARGOS_TOKEN can read build data, but they cannot create reviews."
|
|
409
|
+
].join("\n"));
|
|
410
|
+
}
|
|
411
|
+
if (!data) {
|
|
412
|
+
console.error("Error: Unexpected empty response from API.");
|
|
413
|
+
process.exit(1);
|
|
414
|
+
}
|
|
415
|
+
if (options.json) {
|
|
416
|
+
console.log(JSON.stringify(data, null, 2));
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
printReview(data, buildReferenceDetails.buildNumber);
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
//#endregion
|
|
423
|
+
//#region src/commands/login.ts
|
|
424
|
+
const APP_BASE_URL = process.env["ARGOS_APP_BASE_URL"] ?? "https://app.argos-ci.com/";
|
|
425
|
+
const API_BASE_URL = process.env["ARGOS_API_BASE_URL"] ?? "https://api.argos-ci.com/v2/";
|
|
426
|
+
const LOGIN_CLI_SUCCESS_ROUTE = `/auth/cli/success`;
|
|
427
|
+
const LOGIN_TIMEOUT_MS = 300 * 1e3;
|
|
428
|
+
const CALLBACK_ERROR_HTML = `<!DOCTYPE html><html lang="en">
|
|
429
|
+
<head>
|
|
430
|
+
<meta charset="UTF-8">
|
|
431
|
+
<title>Argos CLI</title>
|
|
432
|
+
</head>
|
|
433
|
+
<body>
|
|
434
|
+
<p>Authorization failed. Please run <code>argos login</code> again.</p>
|
|
435
|
+
</body>
|
|
436
|
+
</html>`;
|
|
437
|
+
function color(text, code) {
|
|
438
|
+
if (!process.stderr.isTTY || process.env["NO_COLOR"]) return text;
|
|
439
|
+
return `\u001B[${code}m${text}\u001B[0m`;
|
|
440
|
+
}
|
|
441
|
+
const successColor = (text) => color(text, 32);
|
|
442
|
+
const warningColor = (text) => color(text, 33);
|
|
443
|
+
const errorColor = (text) => color(text, 31);
|
|
444
|
+
function startCallbackServer() {
|
|
445
|
+
return new Promise((resolve, reject) => {
|
|
446
|
+
let resolveCallback = () => {};
|
|
447
|
+
let rejectCallback = () => {};
|
|
448
|
+
let timeout;
|
|
449
|
+
const callbackPromise = new Promise((res, rej) => {
|
|
450
|
+
resolveCallback = res;
|
|
451
|
+
rejectCallback = rej;
|
|
452
|
+
});
|
|
453
|
+
const closeServer = () => {
|
|
454
|
+
if (timeout) clearTimeout(timeout);
|
|
455
|
+
server.close();
|
|
456
|
+
server.closeAllConnections();
|
|
457
|
+
};
|
|
458
|
+
const server = createServer((req, res) => {
|
|
459
|
+
const url = new URL(req.url ?? "/", "http://localhost");
|
|
460
|
+
if (url.pathname !== "/callback") {
|
|
461
|
+
res.writeHead(404, { Connection: "close" });
|
|
462
|
+
res.end();
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
const callbackError = url.searchParams.get("error");
|
|
466
|
+
const callbackErrorDescription = url.searchParams.get("error_description");
|
|
467
|
+
const code = url.searchParams.get("code");
|
|
468
|
+
const state = url.searchParams.get("state");
|
|
469
|
+
if (callbackError) {
|
|
470
|
+
const message = callbackErrorDescription ?? callbackError;
|
|
471
|
+
res.writeHead(400, {
|
|
472
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
473
|
+
Connection: "close"
|
|
474
|
+
});
|
|
475
|
+
res.end(CALLBACK_ERROR_HTML, () => {
|
|
476
|
+
closeServer();
|
|
477
|
+
rejectCallback(new Error(message));
|
|
478
|
+
});
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
if (!code || !state) {
|
|
482
|
+
res.writeHead(400, {
|
|
483
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
484
|
+
Connection: "close"
|
|
485
|
+
});
|
|
486
|
+
res.end(CALLBACK_ERROR_HTML, () => {
|
|
487
|
+
closeServer();
|
|
488
|
+
rejectCallback(/* @__PURE__ */ new Error("Missing code or state in callback"));
|
|
489
|
+
});
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
res.writeHead(302, {
|
|
493
|
+
Location: new URL(LOGIN_CLI_SUCCESS_ROUTE, APP_BASE_URL).href,
|
|
494
|
+
Connection: "close"
|
|
495
|
+
});
|
|
496
|
+
res.end(() => {
|
|
497
|
+
closeServer();
|
|
498
|
+
resolveCallback({
|
|
499
|
+
code,
|
|
500
|
+
state
|
|
501
|
+
});
|
|
502
|
+
});
|
|
503
|
+
});
|
|
504
|
+
server.on("error", reject);
|
|
505
|
+
server.listen(0, "127.0.0.1", () => {
|
|
506
|
+
const { port } = server.address();
|
|
507
|
+
timeout = setTimeout(() => {
|
|
508
|
+
closeServer();
|
|
509
|
+
rejectCallback(/* @__PURE__ */ new Error("Authentication timed out"));
|
|
510
|
+
}, LOGIN_TIMEOUT_MS);
|
|
511
|
+
timeout.unref();
|
|
512
|
+
resolve({
|
|
513
|
+
port,
|
|
514
|
+
waitForCallback: () => callbackPromise
|
|
515
|
+
});
|
|
516
|
+
});
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
function loginCommand(program) {
|
|
520
|
+
program.command("login").description("Log in to Argos by opening your browser").action(async () => {
|
|
521
|
+
const state = randomBytes(16).toString("hex");
|
|
522
|
+
const codeVerifier = randomBytes(32).toString("base64url");
|
|
523
|
+
const codeChallenge = createHash("sha256").update(codeVerifier).digest("base64url");
|
|
524
|
+
let port;
|
|
525
|
+
let waitForCallback;
|
|
526
|
+
try {
|
|
527
|
+
({port, waitForCallback} = await startCallbackServer());
|
|
528
|
+
} catch (err) {
|
|
529
|
+
console.error(errorColor("Error: Failed to start local callback server."));
|
|
530
|
+
console.error(err);
|
|
531
|
+
process.exit(1);
|
|
532
|
+
}
|
|
533
|
+
const loginUrl = new URL("/auth/cli", APP_BASE_URL);
|
|
534
|
+
loginUrl.searchParams.set("port", port.toString());
|
|
535
|
+
loginUrl.searchParams.set("state", state);
|
|
536
|
+
loginUrl.searchParams.set("pkce", codeChallenge);
|
|
537
|
+
console.log("\nOpening browser for authentication…");
|
|
538
|
+
console.log(`If the browser doesn't open, visit:\n ${loginUrl.href}\n`);
|
|
539
|
+
if (process.env["ARGOS_CLI_DISABLE_BROWSER"] !== "1") await open(loginUrl.href).catch((err) => {
|
|
540
|
+
console.warn(warningColor(`Warning: Failed to open browser — ${err instanceof Error ? err.message : String(err)}`));
|
|
541
|
+
console.log("Hint: Open the URL above manually to continue authentication.");
|
|
542
|
+
});
|
|
543
|
+
let result;
|
|
544
|
+
try {
|
|
545
|
+
result = await waitForCallback();
|
|
546
|
+
} catch (err) {
|
|
547
|
+
console.error(errorColor(err instanceof Error ? `Error: ${err.message}. Please run \`argos login\` again.` : "Error: Authorization failed or was cancelled."));
|
|
548
|
+
process.exit(1);
|
|
549
|
+
}
|
|
550
|
+
if (result.state !== state) {
|
|
551
|
+
console.error(errorColor("Error: State mismatch. Try logging in again."));
|
|
552
|
+
process.exit(1);
|
|
553
|
+
}
|
|
554
|
+
const { data, error } = await createClient({ baseUrl: API_BASE_URL }).POST("/auth/cli/token", { body: {
|
|
555
|
+
code: result.code,
|
|
556
|
+
code_verifier: codeVerifier
|
|
557
|
+
} });
|
|
558
|
+
if (error || !data?.token) {
|
|
559
|
+
console.error(errorColor("Error: Authentication failed. Please run `argos login` again."));
|
|
560
|
+
process.exit(1);
|
|
561
|
+
}
|
|
562
|
+
await saveToken(data.token);
|
|
563
|
+
console.log(successColor("Logged in to Argos successfully."));
|
|
564
|
+
});
|
|
565
|
+
program.command("logout").description("Log out from Argos").action(async () => {
|
|
566
|
+
if (!await getStoredToken()) {
|
|
567
|
+
console.log("No token found. You are already logged out.");
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
await removeToken();
|
|
571
|
+
console.log("Logged out from Argos.");
|
|
572
|
+
});
|
|
283
573
|
}
|
|
284
574
|
//#endregion
|
|
285
575
|
//#region src/index.ts
|
|
@@ -290,6 +580,7 @@ uploadCommand(program);
|
|
|
290
580
|
skipCommand(program);
|
|
291
581
|
finalizeCommand(program);
|
|
292
582
|
buildCommand(program);
|
|
583
|
+
loginCommand(program);
|
|
293
584
|
if (!process.argv.slice(2).length) program.outputHelp();
|
|
294
585
|
else program.parse(process.argv);
|
|
295
586
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@argos-ci/cli",
|
|
3
3
|
"description": "Command-line (CLI) for visual testing with Argos.",
|
|
4
|
-
"version": "4.
|
|
4
|
+
"version": "4.2.0",
|
|
5
5
|
"bin": {
|
|
6
6
|
"argos": "./bin/argos-cli.js"
|
|
7
7
|
},
|
|
@@ -34,18 +34,22 @@
|
|
|
34
34
|
"access": "public"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@argos-ci/api-client": "0.
|
|
38
|
-
"@argos-ci/core": "5.2.1
|
|
37
|
+
"@argos-ci/api-client": "0.18.0",
|
|
38
|
+
"@argos-ci/core": "5.2.1",
|
|
39
39
|
"commander": "^14.0.3",
|
|
40
|
+
"open": "^11.0.0",
|
|
40
41
|
"ora": "^9.3.0",
|
|
41
42
|
"update-notifier": "^7.3.1"
|
|
42
43
|
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"vitest": "catalog:"
|
|
46
|
+
},
|
|
43
47
|
"scripts": {
|
|
44
48
|
"build": "tsdown",
|
|
45
|
-
"e2e": "
|
|
49
|
+
"e2e": "vitest",
|
|
46
50
|
"check-types": "tsc",
|
|
47
51
|
"check-format": "prettier --check --ignore-unknown --ignore-path=../../.gitignore --ignore-path=../../.prettierignore .",
|
|
48
52
|
"lint": "eslint ."
|
|
49
53
|
},
|
|
50
|
-
"gitHead": "
|
|
54
|
+
"gitHead": "906c65c34de9cadf237cee1bf14a925dd960b6c6"
|
|
51
55
|
}
|