@computesdk/cli 1.0.10 → 1.0.12
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 +440 -63
- package/dist/index.js.map +1 -1
- package/package.json +3 -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
|
|
@@ -708,6 +708,76 @@ var ActionsApiError = class extends Error {
|
|
|
708
708
|
this.name = "ActionsApiError";
|
|
709
709
|
}
|
|
710
710
|
};
|
|
711
|
+
var ActionsCliError = class extends Error {
|
|
712
|
+
constructor(code, message) {
|
|
713
|
+
super(message);
|
|
714
|
+
this.code = code;
|
|
715
|
+
this.name = "ActionsCliError";
|
|
716
|
+
}
|
|
717
|
+
};
|
|
718
|
+
function codeForStatus(status) {
|
|
719
|
+
switch (status) {
|
|
720
|
+
case 400:
|
|
721
|
+
return "bad_request";
|
|
722
|
+
case 401:
|
|
723
|
+
return "unauthenticated";
|
|
724
|
+
case 403:
|
|
725
|
+
return "forbidden";
|
|
726
|
+
case 404:
|
|
727
|
+
return "not_found";
|
|
728
|
+
case 409:
|
|
729
|
+
return "conflict";
|
|
730
|
+
case 413:
|
|
731
|
+
return "payload_too_large";
|
|
732
|
+
case 429:
|
|
733
|
+
return "rate_limited";
|
|
734
|
+
default:
|
|
735
|
+
return status >= 500 ? "server_error" : "http_error";
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
function errorDetails(body) {
|
|
739
|
+
if (body && typeof body === "object" && "details" in body) {
|
|
740
|
+
const details = body.details;
|
|
741
|
+
if (details && typeof details === "object" && !Array.isArray(details)) {
|
|
742
|
+
return details;
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
return void 0;
|
|
746
|
+
}
|
|
747
|
+
function toErrorEnvelope(error) {
|
|
748
|
+
if (error instanceof ActionsApiError) {
|
|
749
|
+
const details = errorDetails(error.body);
|
|
750
|
+
return {
|
|
751
|
+
ok: false,
|
|
752
|
+
error: {
|
|
753
|
+
code: codeForStatus(error.status),
|
|
754
|
+
message: error.message,
|
|
755
|
+
httpStatus: error.status,
|
|
756
|
+
retryable: error.status === 429 || error.status === 502 || error.status === 503 || error.status === 504,
|
|
757
|
+
...details !== void 0 && { details }
|
|
758
|
+
}
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
if (error instanceof ActionsCliError) {
|
|
762
|
+
return { ok: false, error: { code: error.code, message: error.message, retryable: false } };
|
|
763
|
+
}
|
|
764
|
+
if (error instanceof TypeError && /fetch failed/i.test(error.message)) {
|
|
765
|
+
const cause = error.cause;
|
|
766
|
+
const detail = cause instanceof Error ? `: ${cause.message}` : "";
|
|
767
|
+
return {
|
|
768
|
+
ok: false,
|
|
769
|
+
error: { code: "network", message: `${error.message}${detail}`, retryable: true }
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
return {
|
|
773
|
+
ok: false,
|
|
774
|
+
error: {
|
|
775
|
+
code: "unknown",
|
|
776
|
+
message: error instanceof Error ? error.message : String(error),
|
|
777
|
+
retryable: false
|
|
778
|
+
}
|
|
779
|
+
};
|
|
780
|
+
}
|
|
711
781
|
var ActionsClient = class {
|
|
712
782
|
constructor(auth, fetchImpl = fetch) {
|
|
713
783
|
this.auth = auth;
|
|
@@ -747,6 +817,14 @@ var ActionsClient = class {
|
|
|
747
817
|
});
|
|
748
818
|
return this.parse(res);
|
|
749
819
|
}
|
|
820
|
+
async patch(path5, body = {}) {
|
|
821
|
+
const res = await this.fetchImpl(`${this.auth.baseUrl}${path5}`, {
|
|
822
|
+
method: "PATCH",
|
|
823
|
+
headers: { ...this.headers(), "content-type": "application/json" },
|
|
824
|
+
body: JSON.stringify(body)
|
|
825
|
+
});
|
|
826
|
+
return this.parse(res);
|
|
827
|
+
}
|
|
750
828
|
async del(path5) {
|
|
751
829
|
const res = await this.fetchImpl(`${this.auth.baseUrl}${path5}`, {
|
|
752
830
|
method: "DELETE",
|
|
@@ -826,22 +904,64 @@ function encodeWatchCursor(id, jobId, step, offset) {
|
|
|
826
904
|
const stepPart = step === null ? "-" : step === "runner" ? "runner" : String(step);
|
|
827
905
|
return `${id}:${jobId}:${stepPart}:${offset}`;
|
|
828
906
|
}
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
if (!apiKey) {
|
|
832
|
-
throw new Error(
|
|
833
|
-
"No API key. Set COMPUTE_API_KEY or pass --api-key."
|
|
834
|
-
);
|
|
835
|
-
}
|
|
907
|
+
var NO_CREDENTIALS_HINT = "Set COMPUTE_API_KEY, pass --api-key, or run `compute bench auth login`.";
|
|
908
|
+
async function resolveActionsAuth(opts, resolveStored = resolveStoredPlatformAuth) {
|
|
836
909
|
const baseUrl = (opts.baseUrl || process.env.COMPUTE_PLATFORM_URL || process.env.BENCHMARKS_PLATFORM_URL || // legacy name
|
|
837
910
|
DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
838
911
|
if (!opts.allowUntrustedHost && !isTrustedActionsHost(baseUrl)) {
|
|
839
|
-
throw new
|
|
912
|
+
throw new ActionsCliError(
|
|
913
|
+
"untrusted_host",
|
|
840
914
|
`Refusing to send the API key to ${baseUrl} \u2014 it is not a computesdk.com or localhost host. If this is a self-hosted/dev deployment you trust, pass --allow-untrusted-host.`
|
|
841
915
|
);
|
|
842
916
|
}
|
|
917
|
+
if (!isSecureActionsTransport(baseUrl)) {
|
|
918
|
+
throw new ActionsCliError(
|
|
919
|
+
"insecure_transport",
|
|
920
|
+
`Refusing to send the API key over plaintext HTTP to ${baseUrl}. Use an https:// base URL; http:// is only allowed for localhost/loopback.`
|
|
921
|
+
);
|
|
922
|
+
}
|
|
923
|
+
let apiKey = opts.apiKey || process.env.COMPUTE_API_KEY || process.env.BENCHMARKS_PLATFORM_API_KEY || // legacy name
|
|
924
|
+
void 0;
|
|
925
|
+
if (!apiKey) {
|
|
926
|
+
if (!isTrustedActionsHost(baseUrl)) {
|
|
927
|
+
throw new ActionsCliError(
|
|
928
|
+
"untrusted_host_stored_auth",
|
|
929
|
+
`Refusing to use stored platform credentials with untrusted host ${baseUrl}. Pass --api-key or set COMPUTE_API_KEY to use an explicit key with --allow-untrusted-host.`
|
|
930
|
+
);
|
|
931
|
+
}
|
|
932
|
+
let stored;
|
|
933
|
+
try {
|
|
934
|
+
stored = await resolveStored({ baseUrl });
|
|
935
|
+
} catch (error) {
|
|
936
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
937
|
+
const detail = reason.startsWith("No credentials found") ? "No API key." : reason;
|
|
938
|
+
throw new ActionsCliError("no_credentials", `${detail} ${NO_CREDENTIALS_HINT}`);
|
|
939
|
+
}
|
|
940
|
+
apiKey = stored.apiKey || stored.token || void 0;
|
|
941
|
+
}
|
|
942
|
+
if (!apiKey) {
|
|
943
|
+
throw new ActionsCliError("no_credentials", `No API key. ${NO_CREDENTIALS_HINT}`);
|
|
944
|
+
}
|
|
843
945
|
return { apiKey, baseUrl };
|
|
844
946
|
}
|
|
947
|
+
async function resolveStoredPlatformAuth(opts) {
|
|
948
|
+
const { resolveAuth } = await import("@benchsdk/cli");
|
|
949
|
+
const auth = await resolveAuth({ baseUrl: opts.baseUrl });
|
|
950
|
+
return { apiKey: auth.apiKey, token: auth.token };
|
|
951
|
+
}
|
|
952
|
+
function isLoopbackHost(host) {
|
|
953
|
+
return host === "localhost" || host === "127.0.0.1" || host === "[::1]" || host === "::1";
|
|
954
|
+
}
|
|
955
|
+
function isSecureActionsTransport(baseUrl) {
|
|
956
|
+
let url;
|
|
957
|
+
try {
|
|
958
|
+
url = new URL(baseUrl);
|
|
959
|
+
} catch {
|
|
960
|
+
return false;
|
|
961
|
+
}
|
|
962
|
+
if (url.protocol === "https:") return true;
|
|
963
|
+
return url.protocol === "http:" && isLoopbackHost(url.hostname);
|
|
964
|
+
}
|
|
845
965
|
function isTrustedActionsHost(baseUrl) {
|
|
846
966
|
let host;
|
|
847
967
|
try {
|
|
@@ -849,7 +969,7 @@ function isTrustedActionsHost(baseUrl) {
|
|
|
849
969
|
} catch {
|
|
850
970
|
return false;
|
|
851
971
|
}
|
|
852
|
-
return host === "computesdk.com" || host.endsWith(".computesdk.com") || host
|
|
972
|
+
return host === "computesdk.com" || host.endsWith(".computesdk.com") || isLoopbackHost(host);
|
|
853
973
|
}
|
|
854
974
|
|
|
855
975
|
// src/actions.ts
|
|
@@ -858,8 +978,8 @@ function sanitizePathPart(name) {
|
|
|
858
978
|
if (base === "" || base === "." || base === "..") return "artifact";
|
|
859
979
|
return base;
|
|
860
980
|
}
|
|
861
|
-
function client(opts) {
|
|
862
|
-
return new ActionsClient(resolveActionsAuth(opts));
|
|
981
|
+
async function client(opts) {
|
|
982
|
+
return new ActionsClient(await resolveActionsAuth(opts));
|
|
863
983
|
}
|
|
864
984
|
function output(opts, data, render) {
|
|
865
985
|
if (opts.json) {
|
|
@@ -892,14 +1012,36 @@ async function listRunJobs(c, runId) {
|
|
|
892
1012
|
steps: []
|
|
893
1013
|
}));
|
|
894
1014
|
}
|
|
895
|
-
function
|
|
896
|
-
|
|
1015
|
+
async function fetchRunSummary(c, runId) {
|
|
1016
|
+
try {
|
|
1017
|
+
return await c.get(`/api/v1/actions/runs/${runId}/summary`);
|
|
1018
|
+
} catch (e) {
|
|
1019
|
+
if (!(e instanceof ActionsApiError && e.status === 404)) {
|
|
1020
|
+
console.error(
|
|
1021
|
+
pc.dim(`summary unavailable: ${e instanceof Error ? e.message : String(e)}`)
|
|
1022
|
+
);
|
|
1023
|
+
}
|
|
1024
|
+
return null;
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
function fail(error, opts = {}) {
|
|
1028
|
+
if (opts.json) {
|
|
1029
|
+
process.stderr.write(JSON.stringify(toErrorEnvelope(error)) + "\n");
|
|
1030
|
+
} else if (error instanceof ActionsApiError) {
|
|
897
1031
|
console.error(pc.red(`Error (${error.status}): ${error.message}`));
|
|
898
1032
|
} else {
|
|
899
|
-
console.error(pc.red(`Error: ${error.message}`));
|
|
1033
|
+
console.error(pc.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
|
|
900
1034
|
}
|
|
901
1035
|
process.exit(1);
|
|
902
1036
|
}
|
|
1037
|
+
function usageErrorOutput(str, write, argv = process.argv) {
|
|
1038
|
+
if (argv.includes("--json")) {
|
|
1039
|
+
const message = str.replace(/^error:\s*/i, "").trim();
|
|
1040
|
+
write(JSON.stringify(toErrorEnvelope(new ActionsCliError("invalid_argument", message))) + "\n");
|
|
1041
|
+
} else {
|
|
1042
|
+
write(str);
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
903
1045
|
function formatDuration(ms) {
|
|
904
1046
|
if (ms === null || ms === void 0) return "-";
|
|
905
1047
|
if (ms < 1e3) return `${ms}ms`;
|
|
@@ -970,12 +1112,102 @@ function formatRunDetail(run, runUrl) {
|
|
|
970
1112
|
}
|
|
971
1113
|
return lines.join("\n");
|
|
972
1114
|
}
|
|
1115
|
+
function formatRunInspection(run) {
|
|
1116
|
+
const lines = [];
|
|
1117
|
+
lines.push(`${pc.bold("run")} ${conclusionLabel(run.conclusion)}${run.runNumber === null ? "" : ` #${run.runNumber}`}`);
|
|
1118
|
+
lines.push(pc.dim(run.id));
|
|
1119
|
+
lines.push(`${safeTerm(run.ref)} ${shortSha(run.headSha)} ${safeTerm(run.event)}`);
|
|
1120
|
+
lines.push(`queued: ${run.queuedAt} started: ${run.startedAt} finished: ${run.finishedAt}`);
|
|
1121
|
+
if (run.cancellationReason) lines.push(`cancelled: ${run.cancellationReason}`);
|
|
1122
|
+
if (run.supersededByRunId) lines.push(`superseded by: ${run.supersededByRunId}`);
|
|
1123
|
+
if (run.blockedReason) lines.push(pc.red(`blocked: ${safeTerm(run.blockedReason)}`));
|
|
1124
|
+
if (run.concurrencyGroup) lines.push(`concurrency: ${safeTerm(run.concurrencyGroup)}`);
|
|
1125
|
+
if (run.providerOverride) lines.push(`dispatched to: ${safeTerm(run.providerOverride)}`);
|
|
1126
|
+
if (run.definitionStale) {
|
|
1127
|
+
const parsed = run.workflowParsedFromSha ? shortSha(run.workflowParsedFromSha) : "unknown";
|
|
1128
|
+
lines.push(pc.yellow(`definition: stored parse is from ${parsed}, not this head \u2014 context may be newer than the run`));
|
|
1129
|
+
}
|
|
1130
|
+
if (run.dispatchInputs && Object.keys(run.dispatchInputs).length > 0) {
|
|
1131
|
+
lines.push(`inputs: ${Object.entries(run.dispatchInputs).map(([k, v]) => `${safeTerm(k)}=${safeTerm(v)}`).join(" ")}`);
|
|
1132
|
+
}
|
|
1133
|
+
if (run.secrets) {
|
|
1134
|
+
const names = run.secrets.names.length > 0 ? run.secrets.names.map(safeTerm).join(", ") : "none";
|
|
1135
|
+
lines.push(`secrets (${run.secrets.access}${run.secrets.env ? ", exported to env" : ""}): ${names}`);
|
|
1136
|
+
}
|
|
1137
|
+
if (run.caches.length > 0) {
|
|
1138
|
+
lines.push("");
|
|
1139
|
+
lines.push(pc.bold("caches"));
|
|
1140
|
+
for (const cache of run.caches) {
|
|
1141
|
+
const action = [cache.restoredAt ? "restored" : null, cache.savedAt ? "saved" : null].filter(Boolean).join("+");
|
|
1142
|
+
lines.push(` ${safeTerm(cache.key)} ${pc.dim(`${action} ${safeTerm(cache.scopeRef)} ${cache.sizeBytes}B`)}`);
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
if (run.jobs.length > 0) {
|
|
1146
|
+
lines.push("");
|
|
1147
|
+
lines.push(pc.bold("jobs"));
|
|
1148
|
+
for (const job of run.jobs) {
|
|
1149
|
+
const placement = job.provider ? `${safeTerm(job.provider)}${job.region ? `:${safeTerm(job.region)}` : ""}` : "-";
|
|
1150
|
+
lines.push(` ${safeTerm(job.name)} ${conclusionLabel(job.state)} ${placement}`);
|
|
1151
|
+
lines.push(pc.dim(` ${job.id}`));
|
|
1152
|
+
if (job.runsOn.length > 0 || job.resolvedRunsOn !== null) {
|
|
1153
|
+
const resolved = job.resolvedRunsOn !== null && job.resolvedRunsOn.join(",") !== job.runsOn.join(",") ? ` \u2192 ${job.resolvedRunsOn.map(safeTerm).join(", ")}` : "";
|
|
1154
|
+
lines.push(` runs-on: ${job.runsOn.map(safeTerm).join(", ") || "(none)"}${resolved}`);
|
|
1155
|
+
}
|
|
1156
|
+
if (job.runsOnHasExpression) lines.push(pc.dim(" runs-on resolves at runtime (expression)"));
|
|
1157
|
+
if (job.container) lines.push(` container: ${safeTerm(job.container)}`);
|
|
1158
|
+
if (job.runnerImage) lines.push(` image: ${safeTerm(job.runnerImage)}`);
|
|
1159
|
+
if (job.placementHint) {
|
|
1160
|
+
lines.push(` pinned: ${safeTerm(job.placementHint.label)}`);
|
|
1161
|
+
}
|
|
1162
|
+
if (job.concurrencyGroup) {
|
|
1163
|
+
lines.push(` concurrency: ${safeTerm(job.concurrencyGroup)}${job.concurrencyCancelInProgress ? " (cancel-in-progress)" : ""}`);
|
|
1164
|
+
}
|
|
1165
|
+
const overrides = [
|
|
1166
|
+
job.timeoutMinutes !== null ? `timeout ${job.timeoutMinutes}m` : null,
|
|
1167
|
+
job.fetchDepth !== null ? `fetch-depth ${job.fetchDepth}` : null
|
|
1168
|
+
].filter(Boolean);
|
|
1169
|
+
if (overrides.length > 0) lines.push(` ${overrides.join(" ")}`);
|
|
1170
|
+
if (job.matrix) {
|
|
1171
|
+
lines.push(` matrix: ${Object.entries(job.matrix).map(([k, v]) => `${safeTerm(k)}=${safeTerm(v)}`).join(" ")}`);
|
|
1172
|
+
}
|
|
1173
|
+
if (job.failureReason) lines.push(pc.red(` failed: ${safeTerm(job.failureReason)}`));
|
|
1174
|
+
for (const attempt of job.placementAttempts) {
|
|
1175
|
+
lines.push(pc.dim(` placement failed on ${safeTerm(attempt.provider)}${attempt.region ? `:${safeTerm(attempt.region)}` : ""}: ${safeTerm(attempt.error)}`));
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
return lines.join("\n");
|
|
1180
|
+
}
|
|
1181
|
+
function formatRunSummary(summary) {
|
|
1182
|
+
const lines = [];
|
|
1183
|
+
lines.push(pc.bold(`failures \u2014 run ${summary.runId} ${conclusionLabel(summary.conclusion)}`));
|
|
1184
|
+
if (summary.failures.length === 0) {
|
|
1185
|
+
lines.push(" no failed jobs");
|
|
1186
|
+
return lines.join("\n");
|
|
1187
|
+
}
|
|
1188
|
+
for (const job of summary.failures) {
|
|
1189
|
+
const placement = job.provider ? `${safeTerm(job.provider)}${job.region ? `:${safeTerm(job.region)}` : ""}` : "-";
|
|
1190
|
+
lines.push(` ${job.id} ${safeTerm(job.name)} ${conclusionLabel(job.conclusion)} ${placement}`);
|
|
1191
|
+
if (job.failureReason) lines.push(pc.dim(` ${safeTerm(job.failureReason)}`));
|
|
1192
|
+
for (const step of job.failedSteps) {
|
|
1193
|
+
const exit = step.exitCode === null ? "" : ` (exit ${step.exitCode})`;
|
|
1194
|
+
lines.push(` step ${step.ordinal} ${safeTerm(step.name)}${exit}`);
|
|
1195
|
+
}
|
|
1196
|
+
if (job.excerpt) {
|
|
1197
|
+
const scope = job.excerpt.stepOrdinal === null ? "job log" : `step ${job.excerpt.stepOrdinal}`;
|
|
1198
|
+
const cut = job.excerpt.truncated ? ", truncated" : "";
|
|
1199
|
+
lines.push(pc.dim(` \u2500\u2500 ${scope} tail${cut} \u2500\u2500`));
|
|
1200
|
+
for (const line of job.excerpt.text.split("\n")) lines.push(` ${safeTerm(line)}`);
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
return lines.join("\n");
|
|
1204
|
+
}
|
|
973
1205
|
function parseInputs(pairs) {
|
|
974
1206
|
const inputs = {};
|
|
975
1207
|
for (const pair of pairs ?? []) {
|
|
976
1208
|
const eq = pair.indexOf("=");
|
|
977
1209
|
if (eq === -1) {
|
|
978
|
-
throw new
|
|
1210
|
+
throw new ActionsCliError("invalid_argument", `Invalid --inputs entry "${pair}". Expected key=value.`);
|
|
979
1211
|
}
|
|
980
1212
|
inputs[pair.slice(0, eq)] = pair.slice(eq + 1);
|
|
981
1213
|
}
|
|
@@ -1025,7 +1257,7 @@ function parseStep(step) {
|
|
|
1025
1257
|
if (step === "runner") return "runner";
|
|
1026
1258
|
const n = Number(step);
|
|
1027
1259
|
if (!Number.isInteger(n) || n < 0) {
|
|
1028
|
-
throw new
|
|
1260
|
+
throw new ActionsCliError("invalid_argument", `Invalid --step "${step}". Expected a step ordinal or "runner".`);
|
|
1029
1261
|
}
|
|
1030
1262
|
return n;
|
|
1031
1263
|
}
|
|
@@ -1109,14 +1341,57 @@ function formatVerifyResult(r) {
|
|
|
1109
1341
|
const act = r.key.actCapable ? pc.green(" act-capable") : "";
|
|
1110
1342
|
return `${head} ${r.key.provider}${act}${detail}`;
|
|
1111
1343
|
}
|
|
1344
|
+
function formatRepoRow(r) {
|
|
1345
|
+
const head = r.enabled ? pc.green("enabled") : pc.dim("disabled");
|
|
1346
|
+
const via = r.authType === "github_app" ? "github-app" : r.authType;
|
|
1347
|
+
const workflows = r.workflowPaths.length > 0 ? `${r.workflowPaths.length} workflow${r.workflowPaths.length === 1 ? "" : "s"}` : "no workflows yet";
|
|
1348
|
+
const error = r.lastPollError ? pc.red(` poll error: ${safeTerm(r.lastPollError)}`) : "";
|
|
1349
|
+
return `${head} ${safeTerm(r.fullName)} ${pc.dim(`${safeTerm(via)} \xB7 ${safeTerm(r.defaultBranch)} \xB7 ${workflows}`)}${error}`;
|
|
1350
|
+
}
|
|
1351
|
+
function printDiscovery(d) {
|
|
1352
|
+
if (d.error) {
|
|
1353
|
+
console.log(pc.yellow(`discovery: ${safeTerm(d.error)} (the poll cron retries)`));
|
|
1354
|
+
return;
|
|
1355
|
+
}
|
|
1356
|
+
console.log(pc.dim(`discovery: ${d.workflowsFound ? "workflows found" : "no workflows yet"}${d.seeded ? ", cursors seeded" : ""}`));
|
|
1357
|
+
}
|
|
1358
|
+
function dispatchBody(workflow, opts) {
|
|
1359
|
+
if (!workflow.dispatchable && !opts.manual) {
|
|
1360
|
+
throw new ActionsCliError(
|
|
1361
|
+
"invalid_argument",
|
|
1362
|
+
`Workflow "${workflow.path}" does not declare workflow_dispatch. Pass --manual to run it anyway (no inputs).`
|
|
1363
|
+
);
|
|
1364
|
+
}
|
|
1365
|
+
if (!workflow.dispatchable && opts.inputs !== void 0 && opts.inputs.length > 0) {
|
|
1366
|
+
throw new ActionsCliError(
|
|
1367
|
+
"invalid_argument",
|
|
1368
|
+
`Workflow "${workflow.path}" has no workflow_dispatch inputs; --manual runs of it take no --inputs.`
|
|
1369
|
+
);
|
|
1370
|
+
}
|
|
1371
|
+
const ref = opts.ref ?? workflow.refs[0];
|
|
1372
|
+
if (!ref) throw new ActionsCliError("invalid_argument", "No --ref given and the workflow has no watched refs.");
|
|
1373
|
+
if (opts.providerRegion !== void 0 && opts.provider === void 0) {
|
|
1374
|
+
throw new ActionsCliError("invalid_argument", "--provider-region requires --provider.");
|
|
1375
|
+
}
|
|
1376
|
+
return {
|
|
1377
|
+
workflowId: workflow.id,
|
|
1378
|
+
ref,
|
|
1379
|
+
inputs: parseInputs(opts.inputs),
|
|
1380
|
+
...opts.manual && { manual: true },
|
|
1381
|
+
...opts.provider !== void 0 && {
|
|
1382
|
+
provider: opts.provider,
|
|
1383
|
+
...opts.providerRegion !== void 0 && { providerRegion: opts.providerRegion }
|
|
1384
|
+
}
|
|
1385
|
+
};
|
|
1386
|
+
}
|
|
1112
1387
|
function registerActionsCommands(program2) {
|
|
1113
|
-
const actions = program2.command("actions").alias("ci").description("Drive the benchmarks-platform Actions API");
|
|
1114
|
-
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
|
|
1388
|
+
const actions = program2.command("actions").alias("ci").description("Drive the benchmarks-platform Actions API").configureOutput({ outputError: usageErrorOutput });
|
|
1389
|
+
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 an explicit --api-key/env key to a non-computesdk, non-localhost --base-url (stored login credentials are never sent)").option("--json", "print machine-readable JSON");
|
|
1115
1390
|
common(
|
|
1116
|
-
actions.command("dispatch").description("Dispatch a workflow_dispatch run").argument("<repo>", "repository in owner/repo format").requiredOption("--workflow <path|name>", "workflow path or name").option("--ref <ref>", "git ref to run (default: the workflow's first watched ref)").option("--inputs <pairs...>", "workflow inputs as key=value").option("--provider <id>", "place the run on one provider (e.g. namespace, vercel:sfo1) instead of the org provider order").option("--provider-region <region>", "region for --provider (same as --provider <id>:<region>)")
|
|
1391
|
+
actions.command("dispatch").description("Dispatch a workflow_dispatch run (or any workflow with --manual)").argument("<repo>", "repository in owner/repo format").requiredOption("--workflow <path|name>", "workflow path or name").option("--ref <ref>", "git ref to run (default: the workflow's first watched ref)").option("--inputs <pairs...>", "workflow inputs as key=value (workflow_dispatch inputs only)").option("--manual", "run a workflow even if it does not declare workflow_dispatch (such runs take no --inputs)").option("--provider <id>", "place the run on one provider (e.g. namespace, vercel:sfo1) instead of the org provider order").option("--provider-region <region>", "region for --provider (same as --provider <id>:<region>)")
|
|
1117
1392
|
).action(async (repo, opts) => {
|
|
1118
1393
|
try {
|
|
1119
|
-
const c = client(opts);
|
|
1394
|
+
const c = await client(opts);
|
|
1120
1395
|
const { workflows } = await c.get(
|
|
1121
1396
|
"/api/v1/actions/workflows",
|
|
1122
1397
|
{ repo }
|
|
@@ -1124,26 +1399,15 @@ function registerActionsCommands(program2) {
|
|
|
1124
1399
|
const workflow = matchWorkflow(workflows, opts.workflow);
|
|
1125
1400
|
if (!workflow) {
|
|
1126
1401
|
const choices = workflows.map((w) => `${w.path} (${w.name})`).join(", ") || "none";
|
|
1127
|
-
throw new
|
|
1128
|
-
|
|
1402
|
+
throw new ActionsCliError(
|
|
1403
|
+
"workflow_not_found",
|
|
1404
|
+
`No workflow "${opts.workflow}" in ${repo}. Available: ${choices}`
|
|
1129
1405
|
);
|
|
1130
1406
|
}
|
|
1131
|
-
const
|
|
1132
|
-
if (!ref) throw new Error("No --ref given and the workflow has no watched refs.");
|
|
1133
|
-
if (opts.providerRegion !== void 0 && opts.provider === void 0) {
|
|
1134
|
-
throw new Error("--provider-region requires --provider.");
|
|
1135
|
-
}
|
|
1407
|
+
const body = dispatchBody(workflow, opts);
|
|
1136
1408
|
const result = await c.post(
|
|
1137
1409
|
"/api/v1/actions/dispatch",
|
|
1138
|
-
|
|
1139
|
-
workflowId: workflow.id,
|
|
1140
|
-
ref,
|
|
1141
|
-
inputs: parseInputs(opts.inputs),
|
|
1142
|
-
...opts.provider !== void 0 && {
|
|
1143
|
-
provider: opts.provider,
|
|
1144
|
-
...opts.providerRegion !== void 0 && { providerRegion: opts.providerRegion }
|
|
1145
|
-
}
|
|
1146
|
-
}
|
|
1410
|
+
body
|
|
1147
1411
|
);
|
|
1148
1412
|
const org = await c.org();
|
|
1149
1413
|
const url = `${c.baseUrl}/${org.slug}/actions/runs/${result.runId}`;
|
|
@@ -1153,14 +1417,14 @@ function registerActionsCommands(program2) {
|
|
|
1153
1417
|
console.log(`url: ${r.url}`);
|
|
1154
1418
|
});
|
|
1155
1419
|
} catch (e) {
|
|
1156
|
-
fail(e);
|
|
1420
|
+
fail(e, opts);
|
|
1157
1421
|
}
|
|
1158
1422
|
});
|
|
1159
1423
|
common(
|
|
1160
1424
|
actions.command("runs").description("List recent runs for a repo").argument("<repo>", "repository in owner/repo format").option("--status <status...>", "filter by conclusion: passed, failed, running, cancelled, none").option("--branch <branch...>", "filter by branch/ref")
|
|
1161
1425
|
).action(async (repo, opts) => {
|
|
1162
1426
|
try {
|
|
1163
|
-
const { runs } = await client(opts).get(
|
|
1427
|
+
const { runs } = await (await client(opts)).get(
|
|
1164
1428
|
"/api/v1/actions/runs",
|
|
1165
1429
|
{ repo, status: opts.status, branch: opts.branch }
|
|
1166
1430
|
);
|
|
@@ -1172,14 +1436,14 @@ function registerActionsCommands(program2) {
|
|
|
1172
1436
|
for (const run of rs) console.log(formatRunRow(run));
|
|
1173
1437
|
});
|
|
1174
1438
|
} catch (e) {
|
|
1175
|
-
fail(e);
|
|
1439
|
+
fail(e, opts);
|
|
1176
1440
|
}
|
|
1177
1441
|
});
|
|
1178
1442
|
common(
|
|
1179
1443
|
actions.command("history").description("Recent run history for a workflow \u2014 per-job/step failure rates (is this failure flaky?)").argument("<repo>", "repository in owner/repo format").requiredOption("--workflow <path|name>", "workflow path or name").option("--branch <branch...>", "narrow the window to branches/refs").option("--job <job>", "narrow the rollup to one job name or workflow job id").option("--limit <n>", "runs in the window (default 10, max 50)")
|
|
1180
1444
|
).action(async (repo, opts) => {
|
|
1181
1445
|
try {
|
|
1182
|
-
const c = client(opts);
|
|
1446
|
+
const c = await client(opts);
|
|
1183
1447
|
const { workflows } = await c.get(
|
|
1184
1448
|
"/api/v1/actions/workflows",
|
|
1185
1449
|
{ repo }
|
|
@@ -1195,27 +1459,57 @@ function registerActionsCommands(program2) {
|
|
|
1195
1459
|
});
|
|
1196
1460
|
output(opts, history, (h) => console.log(formatRunHistory(h)));
|
|
1197
1461
|
} catch (e) {
|
|
1198
|
-
fail(e);
|
|
1462
|
+
fail(e, opts);
|
|
1199
1463
|
}
|
|
1200
1464
|
});
|
|
1201
1465
|
common(
|
|
1202
1466
|
actions.command("run").description("Show a run: jobs, provider:region placement, placement attempts").argument("<run-id>", "run ID")
|
|
1203
1467
|
).action(async (runId, opts) => {
|
|
1204
1468
|
try {
|
|
1205
|
-
const c = client(opts);
|
|
1469
|
+
const c = await client(opts);
|
|
1206
1470
|
const run = await c.get(`/api/v1/actions/runs/${runId}`);
|
|
1207
1471
|
const org = await c.org();
|
|
1208
1472
|
const url = `${c.baseUrl}/${org.slug}/actions/runs/${run.id}`;
|
|
1209
|
-
|
|
1473
|
+
const summary = run.conclusion === "failed" ? await fetchRunSummary(c, runId) : null;
|
|
1474
|
+
output(opts, { ...run, url, summary }, (r) => {
|
|
1475
|
+
console.log(formatRunDetail(r, r.url));
|
|
1476
|
+
if (summary && summary.failures.length > 0) {
|
|
1477
|
+
console.log("");
|
|
1478
|
+
console.log(formatRunSummary(summary));
|
|
1479
|
+
}
|
|
1480
|
+
});
|
|
1210
1481
|
} catch (e) {
|
|
1211
|
-
fail(e);
|
|
1482
|
+
fail(e, opts);
|
|
1483
|
+
}
|
|
1484
|
+
});
|
|
1485
|
+
common(
|
|
1486
|
+
actions.command("summary").description("Show a run's failure digest: failed jobs, failed steps, bounded log tails").argument("<run-id>", "run ID")
|
|
1487
|
+
).action(async (runId, opts) => {
|
|
1488
|
+
try {
|
|
1489
|
+
const summary = await (await client(opts)).get(`/api/v1/actions/runs/${runId}/summary`);
|
|
1490
|
+
output(opts, summary, (s) => console.log(formatRunSummary(s)));
|
|
1491
|
+
} catch (e) {
|
|
1492
|
+
fail(e, opts);
|
|
1493
|
+
}
|
|
1494
|
+
});
|
|
1495
|
+
common(
|
|
1496
|
+
actions.command("inspect").description("Inspect a run: resolved runner image, caches, secret names, concurrency, placement context").argument("<run-id>", "run ID")
|
|
1497
|
+
).action(async (runId, opts) => {
|
|
1498
|
+
try {
|
|
1499
|
+
const c = await client(opts);
|
|
1500
|
+
const inspection = await c.get(
|
|
1501
|
+
`/api/v1/actions/runs/${runId}/state`
|
|
1502
|
+
);
|
|
1503
|
+
output(opts, inspection, (r) => console.log(formatRunInspection(r)));
|
|
1504
|
+
} catch (e) {
|
|
1505
|
+
fail(e, opts);
|
|
1212
1506
|
}
|
|
1213
1507
|
});
|
|
1214
1508
|
common(
|
|
1215
1509
|
actions.command("logs").description("Print or follow a run's job logs").argument("<run-id>", "run ID").option("--job <job>", "job ID or name (default: all jobs)").option("--step <step>", 'step ordinal or "runner" (default: whole job)').option("--follow", "stream logs while the run is live")
|
|
1216
1510
|
).action(async (runId, opts) => {
|
|
1217
1511
|
try {
|
|
1218
|
-
const c = client(opts);
|
|
1512
|
+
const c = await client(opts);
|
|
1219
1513
|
let jobs = await listRunJobs(c, runId);
|
|
1220
1514
|
if (opts.job) {
|
|
1221
1515
|
const job = matchJob(jobs, opts.job);
|
|
@@ -1266,25 +1560,25 @@ function registerActionsCommands(program2) {
|
|
|
1266
1560
|
}
|
|
1267
1561
|
}
|
|
1268
1562
|
} catch (e) {
|
|
1269
|
-
fail(e);
|
|
1563
|
+
fail(e, opts);
|
|
1270
1564
|
}
|
|
1271
1565
|
});
|
|
1272
1566
|
const providers = actions.command("providers").description("List the org's registered compute providers (regions, act/usable status)");
|
|
1273
1567
|
common(providers).action(async (opts) => {
|
|
1274
1568
|
try {
|
|
1275
|
-
const data = await client(opts).get("/api/v1/actions/providers");
|
|
1569
|
+
const data = await (await client(opts)).get("/api/v1/actions/providers");
|
|
1276
1570
|
output(opts, data, (d) => {
|
|
1277
1571
|
for (const p5 of d.providers) console.log(formatProviderRow(p5));
|
|
1278
1572
|
});
|
|
1279
1573
|
} catch (e) {
|
|
1280
|
-
fail(e);
|
|
1574
|
+
fail(e, opts);
|
|
1281
1575
|
}
|
|
1282
1576
|
});
|
|
1283
1577
|
common(
|
|
1284
1578
|
providers.command("configure").description("Save the org's credential for a provider (owner/admin API key)").argument("<provider>", "provider id (e.g. tensorlake, blaxel)").option("--key <key>", "provider API key (single-field credentials)").option("--field <pair...>", "credential fields as name=value (multi-field providers)").option("--verify", "run the placement probe after saving")
|
|
1285
1579
|
).action(async (provider, opts) => {
|
|
1286
1580
|
try {
|
|
1287
|
-
const c = client(opts);
|
|
1581
|
+
const c = await client(opts);
|
|
1288
1582
|
let body;
|
|
1289
1583
|
if (opts.field !== void 0) {
|
|
1290
1584
|
if (opts.key !== void 0) throw new Error("Pass either --key or --field, not both.");
|
|
@@ -1307,55 +1601,138 @@ function registerActionsCommands(program2) {
|
|
|
1307
1601
|
});
|
|
1308
1602
|
if (verification?.verified === false) process.exitCode = 1;
|
|
1309
1603
|
} catch (e) {
|
|
1310
|
-
fail(e);
|
|
1604
|
+
fail(e, opts);
|
|
1311
1605
|
}
|
|
1312
1606
|
});
|
|
1313
1607
|
common(
|
|
1314
1608
|
providers.command("verify").description('Probe the stored provider key with a real sandbox ("Test" in Settings)').argument("<provider>", "provider id")
|
|
1315
1609
|
).action(async (provider, opts) => {
|
|
1316
1610
|
try {
|
|
1317
|
-
const result = await client(opts).post(
|
|
1611
|
+
const result = await (await client(opts)).post(
|
|
1318
1612
|
`/api/v1/actions/providers/${provider}/verify`
|
|
1319
1613
|
);
|
|
1320
1614
|
output(opts, result, (r) => console.log(formatVerifyResult(r)));
|
|
1321
1615
|
if (result.verified === false) process.exitCode = 1;
|
|
1322
1616
|
} catch (e) {
|
|
1323
|
-
fail(e);
|
|
1617
|
+
fail(e, opts);
|
|
1324
1618
|
}
|
|
1325
1619
|
});
|
|
1326
1620
|
common(
|
|
1327
1621
|
providers.command("remove").description("Delete the org's stored credential for a provider").argument("<provider>", "provider id")
|
|
1328
1622
|
).action(async (provider, opts) => {
|
|
1329
1623
|
try {
|
|
1330
|
-
const result = await client(opts).del(
|
|
1624
|
+
const result = await (await client(opts)).del(
|
|
1331
1625
|
`/api/v1/actions/providers/${provider}/key`
|
|
1332
1626
|
);
|
|
1333
1627
|
output(opts, result, (r) => {
|
|
1334
1628
|
console.log(r.deleted ? `deleted ${r.provider}` : `no key stored for ${r.provider}`);
|
|
1335
1629
|
});
|
|
1336
1630
|
} catch (e) {
|
|
1337
|
-
fail(e);
|
|
1631
|
+
fail(e, opts);
|
|
1632
|
+
}
|
|
1633
|
+
});
|
|
1634
|
+
const repos = actions.command("repos").description("List the org's connected repositories");
|
|
1635
|
+
common(repos).action(async (opts) => {
|
|
1636
|
+
try {
|
|
1637
|
+
const data = await (await client(opts)).get("/api/v1/actions/repos");
|
|
1638
|
+
output(opts, data, (d) => {
|
|
1639
|
+
for (const r of d.repos) console.log(formatRepoRow(r));
|
|
1640
|
+
});
|
|
1641
|
+
} catch (e) {
|
|
1642
|
+
fail(e, opts);
|
|
1643
|
+
}
|
|
1644
|
+
});
|
|
1645
|
+
common(
|
|
1646
|
+
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")
|
|
1647
|
+
).action(async (cloneUrl, opts) => {
|
|
1648
|
+
try {
|
|
1649
|
+
let body;
|
|
1650
|
+
if (opts.sshKeyFile !== void 0) {
|
|
1651
|
+
if (opts.token !== void 0 || opts.username !== void 0) {
|
|
1652
|
+
throw new Error("Pass either --ssh-key-file or --token/--username, not both.");
|
|
1653
|
+
}
|
|
1654
|
+
body = {
|
|
1655
|
+
authType: "ssh",
|
|
1656
|
+
credential: readFileSync(opts.sshKeyFile, "utf8"),
|
|
1657
|
+
...opts.knownHostsFile !== void 0 && {
|
|
1658
|
+
knownHosts: readFileSync(opts.knownHostsFile, "utf8")
|
|
1659
|
+
}
|
|
1660
|
+
};
|
|
1661
|
+
} else if (opts.username !== void 0) {
|
|
1662
|
+
if (opts.token === void 0) throw new Error("--username requires --token.");
|
|
1663
|
+
body = { authType: "basic", username: opts.username, credential: opts.token };
|
|
1664
|
+
} else if (opts.token !== void 0) {
|
|
1665
|
+
body = { authType: "token", credential: opts.token };
|
|
1666
|
+
} else {
|
|
1667
|
+
body = { authType: "none" };
|
|
1668
|
+
}
|
|
1669
|
+
if (opts.knownHostsFile !== void 0 && opts.sshKeyFile === void 0) {
|
|
1670
|
+
throw new Error("--known-hosts-file requires --ssh-key-file.");
|
|
1671
|
+
}
|
|
1672
|
+
const result = await (await client(opts)).post("/api/v1/actions/repos", {
|
|
1673
|
+
cloneUrl,
|
|
1674
|
+
...body,
|
|
1675
|
+
...opts.name !== void 0 && { name: opts.name },
|
|
1676
|
+
...opts.branch !== void 0 && { defaultBranch: opts.branch }
|
|
1677
|
+
});
|
|
1678
|
+
output(opts, result, (r) => {
|
|
1679
|
+
console.log(`connected ${pc.cyan(safeTerm(r.fullName))} ${pc.dim(`(${safeTerm(r.authType)}, ${safeTerm(r.defaultBranch)})`)}`);
|
|
1680
|
+
printDiscovery(r.discovery);
|
|
1681
|
+
});
|
|
1682
|
+
} catch (e) {
|
|
1683
|
+
fail(e, opts);
|
|
1684
|
+
}
|
|
1685
|
+
});
|
|
1686
|
+
common(
|
|
1687
|
+
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")
|
|
1688
|
+
).action(async (repo, opts) => {
|
|
1689
|
+
try {
|
|
1690
|
+
const result = await (await client(opts)).patch("/api/v1/actions/repos", {
|
|
1691
|
+
fullName: repo,
|
|
1692
|
+
enabled: true,
|
|
1693
|
+
...opts.repoId !== void 0 && { repoId: opts.repoId }
|
|
1694
|
+
});
|
|
1695
|
+
output(opts, result, (r) => {
|
|
1696
|
+
console.log(`enabled ${pc.cyan(safeTerm(r.fullName))}`);
|
|
1697
|
+
if (r.discovery) printDiscovery(r.discovery);
|
|
1698
|
+
});
|
|
1699
|
+
} catch (e) {
|
|
1700
|
+
fail(e, opts);
|
|
1701
|
+
}
|
|
1702
|
+
});
|
|
1703
|
+
common(
|
|
1704
|
+
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")
|
|
1705
|
+
).action(async (repo, opts) => {
|
|
1706
|
+
try {
|
|
1707
|
+
const result = await (await client(opts)).patch("/api/v1/actions/repos", {
|
|
1708
|
+
fullName: repo,
|
|
1709
|
+
enabled: false,
|
|
1710
|
+
...opts.repoId !== void 0 && { repoId: opts.repoId }
|
|
1711
|
+
});
|
|
1712
|
+
output(opts, result, (r) => console.log(`disabled ${pc.cyan(safeTerm(r.fullName))}`));
|
|
1713
|
+
} catch (e) {
|
|
1714
|
+
fail(e, opts);
|
|
1338
1715
|
}
|
|
1339
1716
|
});
|
|
1340
1717
|
common(
|
|
1341
1718
|
actions.command("cancel").description("Cancel a run").argument("<run-id>", "run ID")
|
|
1342
1719
|
).action(async (runId, opts) => {
|
|
1343
1720
|
try {
|
|
1344
|
-
const result = await client(opts).post(
|
|
1721
|
+
const result = await (await client(opts)).post(
|
|
1345
1722
|
`/api/v1/actions/runs/${runId}/cancel`
|
|
1346
1723
|
);
|
|
1347
1724
|
output(opts, result, (r) => {
|
|
1348
1725
|
console.log(r.cancelled ? `Cancelled ${runId}` : `Run ${runId} was already finished`);
|
|
1349
1726
|
});
|
|
1350
1727
|
} catch (e) {
|
|
1351
|
-
fail(e);
|
|
1728
|
+
fail(e, opts);
|
|
1352
1729
|
}
|
|
1353
1730
|
});
|
|
1354
1731
|
common(
|
|
1355
1732
|
actions.command("rerun").description("Re-run a finished run at the same commit").argument("<run-id>", "run ID")
|
|
1356
1733
|
).action(async (runId, opts) => {
|
|
1357
1734
|
try {
|
|
1358
|
-
const c = client(opts);
|
|
1735
|
+
const c = await client(opts);
|
|
1359
1736
|
const result = await c.post(
|
|
1360
1737
|
`/api/v1/actions/runs/${runId}/rerun`
|
|
1361
1738
|
);
|
|
@@ -1366,14 +1743,14 @@ function registerActionsCommands(program2) {
|
|
|
1366
1743
|
console.log(`url: ${r.url}`);
|
|
1367
1744
|
});
|
|
1368
1745
|
} catch (e) {
|
|
1369
|
-
fail(e);
|
|
1746
|
+
fail(e, opts);
|
|
1370
1747
|
}
|
|
1371
1748
|
});
|
|
1372
1749
|
common(
|
|
1373
1750
|
actions.command("artifacts").description("List or download a run's artifacts").argument("<run-id>", "run ID").option("--job <job>", "job ID or name (default: all jobs)").option("--out <dir>", "download artifacts into this directory instead of listing")
|
|
1374
1751
|
).action(async (runId, opts) => {
|
|
1375
1752
|
try {
|
|
1376
|
-
const c = client(opts);
|
|
1753
|
+
const c = await client(opts);
|
|
1377
1754
|
let jobs = await listRunJobs(c, runId);
|
|
1378
1755
|
if (opts.job) {
|
|
1379
1756
|
const job = matchJob(jobs, opts.job);
|
|
@@ -1422,7 +1799,7 @@ function registerActionsCommands(program2) {
|
|
|
1422
1799
|
if (!any) console.log("No artifacts.");
|
|
1423
1800
|
});
|
|
1424
1801
|
} catch (e) {
|
|
1425
|
-
fail(e);
|
|
1802
|
+
fail(e, opts);
|
|
1426
1803
|
}
|
|
1427
1804
|
});
|
|
1428
1805
|
}
|
|
@@ -1670,7 +2047,7 @@ init_workspace_cli();
|
|
|
1670
2047
|
var __filename2 = fileURLToPath2(import.meta.url);
|
|
1671
2048
|
var __dirname2 = dirname2(__filename2);
|
|
1672
2049
|
var packageJson = JSON.parse(
|
|
1673
|
-
|
|
2050
|
+
readFileSync5(join5(__dirname2, "..", "package.json"), "utf-8")
|
|
1674
2051
|
);
|
|
1675
2052
|
var isDevBuild = import.meta.url.includes("worktrees") || import.meta.url.includes("/packages/cli/");
|
|
1676
2053
|
var VERSION = isDevBuild ? `${packageJson.version}-dev` : packageJson.version;
|