@computesdk/cli 1.0.10 → 1.0.11
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.js +241 -4
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -587,7 +587,7 @@ import "dotenv/config";
|
|
|
587
587
|
import { Command } from "commander";
|
|
588
588
|
import * as p4 from "@clack/prompts";
|
|
589
589
|
import pc6 from "picocolors";
|
|
590
|
-
import { readFileSync as
|
|
590
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
591
591
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
592
592
|
import { dirname as dirname2, join as join5 } from "path";
|
|
593
593
|
import { compute as compute4 } from "computesdk";
|
|
@@ -694,7 +694,7 @@ function buildProviderConfig(provider) {
|
|
|
694
694
|
// src/actions.ts
|
|
695
695
|
init_esm_shims();
|
|
696
696
|
import pc from "picocolors";
|
|
697
|
-
import { mkdirSync, writeFileSync } from "fs";
|
|
697
|
+
import { mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
698
698
|
import { basename, join } from "path";
|
|
699
699
|
|
|
700
700
|
// src/actions-client.ts
|
|
@@ -747,6 +747,14 @@ var ActionsClient = class {
|
|
|
747
747
|
});
|
|
748
748
|
return this.parse(res);
|
|
749
749
|
}
|
|
750
|
+
async patch(path5, body = {}) {
|
|
751
|
+
const res = await this.fetchImpl(`${this.auth.baseUrl}${path5}`, {
|
|
752
|
+
method: "PATCH",
|
|
753
|
+
headers: { ...this.headers(), "content-type": "application/json" },
|
|
754
|
+
body: JSON.stringify(body)
|
|
755
|
+
});
|
|
756
|
+
return this.parse(res);
|
|
757
|
+
}
|
|
750
758
|
async del(path5) {
|
|
751
759
|
const res = await this.fetchImpl(`${this.auth.baseUrl}${path5}`, {
|
|
752
760
|
method: "DELETE",
|
|
@@ -892,6 +900,18 @@ async function listRunJobs(c, runId) {
|
|
|
892
900
|
steps: []
|
|
893
901
|
}));
|
|
894
902
|
}
|
|
903
|
+
async function fetchRunSummary(c, runId) {
|
|
904
|
+
try {
|
|
905
|
+
return await c.get(`/api/v1/actions/runs/${runId}/summary`);
|
|
906
|
+
} catch (e) {
|
|
907
|
+
if (!(e instanceof ActionsApiError && e.status === 404)) {
|
|
908
|
+
console.error(
|
|
909
|
+
pc.dim(`summary unavailable: ${e instanceof Error ? e.message : String(e)}`)
|
|
910
|
+
);
|
|
911
|
+
}
|
|
912
|
+
return null;
|
|
913
|
+
}
|
|
914
|
+
}
|
|
895
915
|
function fail(error) {
|
|
896
916
|
if (error instanceof ActionsApiError) {
|
|
897
917
|
console.error(pc.red(`Error (${error.status}): ${error.message}`));
|
|
@@ -970,6 +990,96 @@ function formatRunDetail(run, runUrl) {
|
|
|
970
990
|
}
|
|
971
991
|
return lines.join("\n");
|
|
972
992
|
}
|
|
993
|
+
function formatRunInspection(run) {
|
|
994
|
+
const lines = [];
|
|
995
|
+
lines.push(`${pc.bold("run")} ${conclusionLabel(run.conclusion)}${run.runNumber === null ? "" : ` #${run.runNumber}`}`);
|
|
996
|
+
lines.push(pc.dim(run.id));
|
|
997
|
+
lines.push(`${safeTerm(run.ref)} ${shortSha(run.headSha)} ${safeTerm(run.event)}`);
|
|
998
|
+
lines.push(`queued: ${run.queuedAt} started: ${run.startedAt} finished: ${run.finishedAt}`);
|
|
999
|
+
if (run.cancellationReason) lines.push(`cancelled: ${run.cancellationReason}`);
|
|
1000
|
+
if (run.supersededByRunId) lines.push(`superseded by: ${run.supersededByRunId}`);
|
|
1001
|
+
if (run.blockedReason) lines.push(pc.red(`blocked: ${safeTerm(run.blockedReason)}`));
|
|
1002
|
+
if (run.concurrencyGroup) lines.push(`concurrency: ${safeTerm(run.concurrencyGroup)}`);
|
|
1003
|
+
if (run.providerOverride) lines.push(`dispatched to: ${safeTerm(run.providerOverride)}`);
|
|
1004
|
+
if (run.definitionStale) {
|
|
1005
|
+
const parsed = run.workflowParsedFromSha ? shortSha(run.workflowParsedFromSha) : "unknown";
|
|
1006
|
+
lines.push(pc.yellow(`definition: stored parse is from ${parsed}, not this head \u2014 context may be newer than the run`));
|
|
1007
|
+
}
|
|
1008
|
+
if (run.dispatchInputs && Object.keys(run.dispatchInputs).length > 0) {
|
|
1009
|
+
lines.push(`inputs: ${Object.entries(run.dispatchInputs).map(([k, v]) => `${safeTerm(k)}=${safeTerm(v)}`).join(" ")}`);
|
|
1010
|
+
}
|
|
1011
|
+
if (run.secrets) {
|
|
1012
|
+
const names = run.secrets.names.length > 0 ? run.secrets.names.map(safeTerm).join(", ") : "none";
|
|
1013
|
+
lines.push(`secrets (${run.secrets.access}${run.secrets.env ? ", exported to env" : ""}): ${names}`);
|
|
1014
|
+
}
|
|
1015
|
+
if (run.caches.length > 0) {
|
|
1016
|
+
lines.push("");
|
|
1017
|
+
lines.push(pc.bold("caches"));
|
|
1018
|
+
for (const cache of run.caches) {
|
|
1019
|
+
const action = [cache.restoredAt ? "restored" : null, cache.savedAt ? "saved" : null].filter(Boolean).join("+");
|
|
1020
|
+
lines.push(` ${safeTerm(cache.key)} ${pc.dim(`${action} ${safeTerm(cache.scopeRef)} ${cache.sizeBytes}B`)}`);
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
if (run.jobs.length > 0) {
|
|
1024
|
+
lines.push("");
|
|
1025
|
+
lines.push(pc.bold("jobs"));
|
|
1026
|
+
for (const job of run.jobs) {
|
|
1027
|
+
const placement = job.provider ? `${safeTerm(job.provider)}${job.region ? `:${safeTerm(job.region)}` : ""}` : "-";
|
|
1028
|
+
lines.push(` ${safeTerm(job.name)} ${conclusionLabel(job.state)} ${placement}`);
|
|
1029
|
+
lines.push(pc.dim(` ${job.id}`));
|
|
1030
|
+
if (job.runsOn.length > 0 || job.resolvedRunsOn !== null) {
|
|
1031
|
+
const resolved = job.resolvedRunsOn !== null && job.resolvedRunsOn.join(",") !== job.runsOn.join(",") ? ` \u2192 ${job.resolvedRunsOn.map(safeTerm).join(", ")}` : "";
|
|
1032
|
+
lines.push(` runs-on: ${job.runsOn.map(safeTerm).join(", ") || "(none)"}${resolved}`);
|
|
1033
|
+
}
|
|
1034
|
+
if (job.runsOnHasExpression) lines.push(pc.dim(" runs-on resolves at runtime (expression)"));
|
|
1035
|
+
if (job.container) lines.push(` container: ${safeTerm(job.container)}`);
|
|
1036
|
+
if (job.runnerImage) lines.push(` image: ${safeTerm(job.runnerImage)}`);
|
|
1037
|
+
if (job.placementHint) {
|
|
1038
|
+
lines.push(` pinned: ${safeTerm(job.placementHint.label)}`);
|
|
1039
|
+
}
|
|
1040
|
+
if (job.concurrencyGroup) {
|
|
1041
|
+
lines.push(` concurrency: ${safeTerm(job.concurrencyGroup)}${job.concurrencyCancelInProgress ? " (cancel-in-progress)" : ""}`);
|
|
1042
|
+
}
|
|
1043
|
+
const overrides = [
|
|
1044
|
+
job.timeoutMinutes !== null ? `timeout ${job.timeoutMinutes}m` : null,
|
|
1045
|
+
job.fetchDepth !== null ? `fetch-depth ${job.fetchDepth}` : null
|
|
1046
|
+
].filter(Boolean);
|
|
1047
|
+
if (overrides.length > 0) lines.push(` ${overrides.join(" ")}`);
|
|
1048
|
+
if (job.matrix) {
|
|
1049
|
+
lines.push(` matrix: ${Object.entries(job.matrix).map(([k, v]) => `${safeTerm(k)}=${safeTerm(v)}`).join(" ")}`);
|
|
1050
|
+
}
|
|
1051
|
+
if (job.failureReason) lines.push(pc.red(` failed: ${safeTerm(job.failureReason)}`));
|
|
1052
|
+
for (const attempt of job.placementAttempts) {
|
|
1053
|
+
lines.push(pc.dim(` placement failed on ${safeTerm(attempt.provider)}${attempt.region ? `:${safeTerm(attempt.region)}` : ""}: ${safeTerm(attempt.error)}`));
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
return lines.join("\n");
|
|
1058
|
+
}
|
|
1059
|
+
function formatRunSummary(summary) {
|
|
1060
|
+
const lines = [];
|
|
1061
|
+
lines.push(pc.bold(`failures \u2014 run ${summary.runId} ${conclusionLabel(summary.conclusion)}`));
|
|
1062
|
+
if (summary.failures.length === 0) {
|
|
1063
|
+
lines.push(" no failed jobs");
|
|
1064
|
+
return lines.join("\n");
|
|
1065
|
+
}
|
|
1066
|
+
for (const job of summary.failures) {
|
|
1067
|
+
const placement = job.provider ? `${safeTerm(job.provider)}${job.region ? `:${safeTerm(job.region)}` : ""}` : "-";
|
|
1068
|
+
lines.push(` ${job.id} ${safeTerm(job.name)} ${conclusionLabel(job.conclusion)} ${placement}`);
|
|
1069
|
+
if (job.failureReason) lines.push(pc.dim(` ${safeTerm(job.failureReason)}`));
|
|
1070
|
+
for (const step of job.failedSteps) {
|
|
1071
|
+
const exit = step.exitCode === null ? "" : ` (exit ${step.exitCode})`;
|
|
1072
|
+
lines.push(` step ${step.ordinal} ${safeTerm(step.name)}${exit}`);
|
|
1073
|
+
}
|
|
1074
|
+
if (job.excerpt) {
|
|
1075
|
+
const scope = job.excerpt.stepOrdinal === null ? "job log" : `step ${job.excerpt.stepOrdinal}`;
|
|
1076
|
+
const cut = job.excerpt.truncated ? ", truncated" : "";
|
|
1077
|
+
lines.push(pc.dim(` \u2500\u2500 ${scope} tail${cut} \u2500\u2500`));
|
|
1078
|
+
for (const line of job.excerpt.text.split("\n")) lines.push(` ${safeTerm(line)}`);
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
return lines.join("\n");
|
|
1082
|
+
}
|
|
973
1083
|
function parseInputs(pairs) {
|
|
974
1084
|
const inputs = {};
|
|
975
1085
|
for (const pair of pairs ?? []) {
|
|
@@ -1109,6 +1219,20 @@ function formatVerifyResult(r) {
|
|
|
1109
1219
|
const act = r.key.actCapable ? pc.green(" act-capable") : "";
|
|
1110
1220
|
return `${head} ${r.key.provider}${act}${detail}`;
|
|
1111
1221
|
}
|
|
1222
|
+
function formatRepoRow(r) {
|
|
1223
|
+
const head = r.enabled ? pc.green("enabled") : pc.dim("disabled");
|
|
1224
|
+
const via = r.authType === "github_app" ? "github-app" : r.authType;
|
|
1225
|
+
const workflows = r.workflowPaths.length > 0 ? `${r.workflowPaths.length} workflow${r.workflowPaths.length === 1 ? "" : "s"}` : "no workflows yet";
|
|
1226
|
+
const error = r.lastPollError ? pc.red(` poll error: ${safeTerm(r.lastPollError)}`) : "";
|
|
1227
|
+
return `${head} ${safeTerm(r.fullName)} ${pc.dim(`${safeTerm(via)} \xB7 ${safeTerm(r.defaultBranch)} \xB7 ${workflows}`)}${error}`;
|
|
1228
|
+
}
|
|
1229
|
+
function printDiscovery(d) {
|
|
1230
|
+
if (d.error) {
|
|
1231
|
+
console.log(pc.yellow(`discovery: ${safeTerm(d.error)} (the poll cron retries)`));
|
|
1232
|
+
return;
|
|
1233
|
+
}
|
|
1234
|
+
console.log(pc.dim(`discovery: ${d.workflowsFound ? "workflows found" : "no workflows yet"}${d.seeded ? ", cursors seeded" : ""}`));
|
|
1235
|
+
}
|
|
1112
1236
|
function registerActionsCommands(program2) {
|
|
1113
1237
|
const actions = program2.command("actions").alias("ci").description("Drive the benchmarks-platform Actions API");
|
|
1114
1238
|
const common = (cmd) => cmd.option("--api-key <key>", "API key (default: $COMPUTE_API_KEY)").option("--base-url <url>", "API base URL (default: https://platform.computesdk.com)").option("--allow-untrusted-host", "send the API key to a non-computesdk, non-localhost --base-url").option("--json", "print machine-readable JSON");
|
|
@@ -1206,7 +1330,37 @@ function registerActionsCommands(program2) {
|
|
|
1206
1330
|
const run = await c.get(`/api/v1/actions/runs/${runId}`);
|
|
1207
1331
|
const org = await c.org();
|
|
1208
1332
|
const url = `${c.baseUrl}/${org.slug}/actions/runs/${run.id}`;
|
|
1209
|
-
|
|
1333
|
+
const summary = run.conclusion === "failed" ? await fetchRunSummary(c, runId) : null;
|
|
1334
|
+
output(opts, { ...run, url, summary }, (r) => {
|
|
1335
|
+
console.log(formatRunDetail(r, r.url));
|
|
1336
|
+
if (summary && summary.failures.length > 0) {
|
|
1337
|
+
console.log("");
|
|
1338
|
+
console.log(formatRunSummary(summary));
|
|
1339
|
+
}
|
|
1340
|
+
});
|
|
1341
|
+
} catch (e) {
|
|
1342
|
+
fail(e);
|
|
1343
|
+
}
|
|
1344
|
+
});
|
|
1345
|
+
common(
|
|
1346
|
+
actions.command("summary").description("Show a run's failure digest: failed jobs, failed steps, bounded log tails").argument("<run-id>", "run ID")
|
|
1347
|
+
).action(async (runId, opts) => {
|
|
1348
|
+
try {
|
|
1349
|
+
const summary = await client(opts).get(`/api/v1/actions/runs/${runId}/summary`);
|
|
1350
|
+
output(opts, summary, (s) => console.log(formatRunSummary(s)));
|
|
1351
|
+
} catch (e) {
|
|
1352
|
+
fail(e);
|
|
1353
|
+
}
|
|
1354
|
+
});
|
|
1355
|
+
common(
|
|
1356
|
+
actions.command("inspect").description("Inspect a run: resolved runner image, caches, secret names, concurrency, placement context").argument("<run-id>", "run ID")
|
|
1357
|
+
).action(async (runId, opts) => {
|
|
1358
|
+
try {
|
|
1359
|
+
const c = client(opts);
|
|
1360
|
+
const inspection = await c.get(
|
|
1361
|
+
`/api/v1/actions/runs/${runId}/state`
|
|
1362
|
+
);
|
|
1363
|
+
output(opts, inspection, (r) => console.log(formatRunInspection(r)));
|
|
1210
1364
|
} catch (e) {
|
|
1211
1365
|
fail(e);
|
|
1212
1366
|
}
|
|
@@ -1337,6 +1491,89 @@ function registerActionsCommands(program2) {
|
|
|
1337
1491
|
fail(e);
|
|
1338
1492
|
}
|
|
1339
1493
|
});
|
|
1494
|
+
const repos = actions.command("repos").description("List the org's connected repositories");
|
|
1495
|
+
common(repos).action(async (opts) => {
|
|
1496
|
+
try {
|
|
1497
|
+
const data = await client(opts).get("/api/v1/actions/repos");
|
|
1498
|
+
output(opts, data, (d) => {
|
|
1499
|
+
for (const r of d.repos) console.log(formatRepoRow(r));
|
|
1500
|
+
});
|
|
1501
|
+
} catch (e) {
|
|
1502
|
+
fail(e);
|
|
1503
|
+
}
|
|
1504
|
+
});
|
|
1505
|
+
common(
|
|
1506
|
+
repos.command("connect").description("Connect a git remote (validated by a real ls-remote; lands enabled)").argument("<cloneUrl>", "http(s) or ssh git remote URL").option("--name <owner/repo>", "repo name override (default: derived from cloneUrl)").option("--branch <branch>", "default branch (default: main, else the remote's first)").option("--token <token>", "credential for private http(s) remotes").option("--username <user>", 'username \u2014 with --token, makes auth "basic" instead of "token"').option("--ssh-key-file <path>", "OpenSSH/PEM private key file for ssh remotes").option("--known-hosts-file <path>", "known_hosts pin for ssh remotes")
|
|
1507
|
+
).action(async (cloneUrl, opts) => {
|
|
1508
|
+
try {
|
|
1509
|
+
let body;
|
|
1510
|
+
if (opts.sshKeyFile !== void 0) {
|
|
1511
|
+
if (opts.token !== void 0 || opts.username !== void 0) {
|
|
1512
|
+
throw new Error("Pass either --ssh-key-file or --token/--username, not both.");
|
|
1513
|
+
}
|
|
1514
|
+
body = {
|
|
1515
|
+
authType: "ssh",
|
|
1516
|
+
credential: readFileSync(opts.sshKeyFile, "utf8"),
|
|
1517
|
+
...opts.knownHostsFile !== void 0 && {
|
|
1518
|
+
knownHosts: readFileSync(opts.knownHostsFile, "utf8")
|
|
1519
|
+
}
|
|
1520
|
+
};
|
|
1521
|
+
} else if (opts.username !== void 0) {
|
|
1522
|
+
if (opts.token === void 0) throw new Error("--username requires --token.");
|
|
1523
|
+
body = { authType: "basic", username: opts.username, credential: opts.token };
|
|
1524
|
+
} else if (opts.token !== void 0) {
|
|
1525
|
+
body = { authType: "token", credential: opts.token };
|
|
1526
|
+
} else {
|
|
1527
|
+
body = { authType: "none" };
|
|
1528
|
+
}
|
|
1529
|
+
if (opts.knownHostsFile !== void 0 && opts.sshKeyFile === void 0) {
|
|
1530
|
+
throw new Error("--known-hosts-file requires --ssh-key-file.");
|
|
1531
|
+
}
|
|
1532
|
+
const result = await client(opts).post("/api/v1/actions/repos", {
|
|
1533
|
+
cloneUrl,
|
|
1534
|
+
...body,
|
|
1535
|
+
...opts.name !== void 0 && { name: opts.name },
|
|
1536
|
+
...opts.branch !== void 0 && { defaultBranch: opts.branch }
|
|
1537
|
+
});
|
|
1538
|
+
output(opts, result, (r) => {
|
|
1539
|
+
console.log(`connected ${pc.cyan(safeTerm(r.fullName))} ${pc.dim(`(${safeTerm(r.authType)}, ${safeTerm(r.defaultBranch)})`)}`);
|
|
1540
|
+
printDiscovery(r.discovery);
|
|
1541
|
+
});
|
|
1542
|
+
} catch (e) {
|
|
1543
|
+
fail(e);
|
|
1544
|
+
}
|
|
1545
|
+
});
|
|
1546
|
+
common(
|
|
1547
|
+
repos.command("enable").description("Enable a repo the org can already see (granted by a GitHub App install, or a connected remote)").argument("<repo>", "repository in owner/repo format").option("--repo-id <id>", "scope to one row when two installations grant the same name")
|
|
1548
|
+
).action(async (repo, opts) => {
|
|
1549
|
+
try {
|
|
1550
|
+
const result = await client(opts).patch("/api/v1/actions/repos", {
|
|
1551
|
+
fullName: repo,
|
|
1552
|
+
enabled: true,
|
|
1553
|
+
...opts.repoId !== void 0 && { repoId: opts.repoId }
|
|
1554
|
+
});
|
|
1555
|
+
output(opts, result, (r) => {
|
|
1556
|
+
console.log(`enabled ${pc.cyan(safeTerm(r.fullName))}`);
|
|
1557
|
+
if (r.discovery) printDiscovery(r.discovery);
|
|
1558
|
+
});
|
|
1559
|
+
} catch (e) {
|
|
1560
|
+
fail(e);
|
|
1561
|
+
}
|
|
1562
|
+
});
|
|
1563
|
+
common(
|
|
1564
|
+
repos.command("disable").description("Disable a repo \u2014 it stays connected but no runs are scheduled").argument("<repo>", "repository in owner/repo format").option("--repo-id <id>", "scope to one row when two installations grant the same name")
|
|
1565
|
+
).action(async (repo, opts) => {
|
|
1566
|
+
try {
|
|
1567
|
+
const result = await client(opts).patch("/api/v1/actions/repos", {
|
|
1568
|
+
fullName: repo,
|
|
1569
|
+
enabled: false,
|
|
1570
|
+
...opts.repoId !== void 0 && { repoId: opts.repoId }
|
|
1571
|
+
});
|
|
1572
|
+
output(opts, result, (r) => console.log(`disabled ${pc.cyan(safeTerm(r.fullName))}`));
|
|
1573
|
+
} catch (e) {
|
|
1574
|
+
fail(e);
|
|
1575
|
+
}
|
|
1576
|
+
});
|
|
1340
1577
|
common(
|
|
1341
1578
|
actions.command("cancel").description("Cancel a run").argument("<run-id>", "run ID")
|
|
1342
1579
|
).action(async (runId, opts) => {
|
|
@@ -1670,7 +1907,7 @@ init_workspace_cli();
|
|
|
1670
1907
|
var __filename2 = fileURLToPath2(import.meta.url);
|
|
1671
1908
|
var __dirname2 = dirname2(__filename2);
|
|
1672
1909
|
var packageJson = JSON.parse(
|
|
1673
|
-
|
|
1910
|
+
readFileSync5(join5(__dirname2, "..", "package.json"), "utf-8")
|
|
1674
1911
|
);
|
|
1675
1912
|
var isDevBuild = import.meta.url.includes("worktrees") || import.meta.url.includes("/packages/cli/");
|
|
1676
1913
|
var VERSION = isDevBuild ? `${packageJson.version}-dev` : packageJson.version;
|