@coderook/cli 0.11.0 → 0.12.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/cli/src/api.js +43 -0
- package/dist/cli/src/cli.js +32 -2
- package/dist/cli/src/service_commands.js +112 -0
- package/package.json +1 -1
package/dist/cli/src/api.js
CHANGED
|
@@ -20,6 +20,8 @@ exports.invite = invite;
|
|
|
20
20
|
exports.watch = watch;
|
|
21
21
|
exports.setWatch = setWatch;
|
|
22
22
|
exports.workflows = workflows;
|
|
23
|
+
exports.runs = runs;
|
|
24
|
+
exports.runLogs = runLogs;
|
|
23
25
|
exports.tokens = tokens;
|
|
24
26
|
exports.revokeToken = revokeToken;
|
|
25
27
|
exports.deleteProject = deleteProject;
|
|
@@ -242,6 +244,47 @@ async function workflows(repositoryId) {
|
|
|
242
244
|
archivedAt: row.archivedAt ? String(row.archivedAt) : null,
|
|
243
245
|
}));
|
|
244
246
|
}
|
|
247
|
+
/**
|
|
248
|
+
* What has run on a project.
|
|
249
|
+
*
|
|
250
|
+
* The same route the workflow list comes from; the service returns both and
|
|
251
|
+
* the CLI was throwing half of it away. Filters are passed through rather
|
|
252
|
+
* than applied here, because a project with a thousand runs should not send
|
|
253
|
+
* a thousand rows to show ten.
|
|
254
|
+
*/
|
|
255
|
+
async function runs(repositoryId, filter = {}) {
|
|
256
|
+
const query = new URLSearchParams();
|
|
257
|
+
if (filter.status)
|
|
258
|
+
query.set("status", filter.status);
|
|
259
|
+
if (filter.workflow)
|
|
260
|
+
query.set("workflow", filter.workflow);
|
|
261
|
+
const suffix = query.size ? `?${query.toString()}` : "";
|
|
262
|
+
const body = await call(`/v1/repositories/${encodeURIComponent(repositoryId)}/workflows${suffix}`);
|
|
263
|
+
return (body.runs ?? []).map((row) => {
|
|
264
|
+
const workflow = (row.workflow ?? {});
|
|
265
|
+
const version = (row.version ?? null);
|
|
266
|
+
return {
|
|
267
|
+
id: String(row.id ?? ""),
|
|
268
|
+
number: Number(row.number ?? 0),
|
|
269
|
+
workflow: String(workflow.name ?? ""),
|
|
270
|
+
version: version ? Number(version.sequence ?? 0) : null,
|
|
271
|
+
status: String(row.status ?? ""),
|
|
272
|
+
summary: String(row.summary ?? ""),
|
|
273
|
+
claimedBy: row.claimedBy ? String(row.claimedBy) : null,
|
|
274
|
+
startedAt: row.startedAt ? String(row.startedAt) : null,
|
|
275
|
+
finishedAt: row.finishedAt ? String(row.finishedAt) : null,
|
|
276
|
+
durationMs: row.durationMs === null || row.durationMs === undefined
|
|
277
|
+
? null
|
|
278
|
+
: Number(row.durationMs),
|
|
279
|
+
};
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
/** Everything a run printed, in the order it printed it. */
|
|
283
|
+
async function runLogs(repositoryId, runId) {
|
|
284
|
+
const body = await call(`/v1/repositories/${encodeURIComponent(repositoryId)}/runs/` +
|
|
285
|
+
`${encodeURIComponent(runId)}/logs`);
|
|
286
|
+
return body.lines ?? [];
|
|
287
|
+
}
|
|
245
288
|
/** Personal access tokens on the account. */
|
|
246
289
|
async function tokens() {
|
|
247
290
|
const body = await call("/v1/account/tokens");
|
package/dist/cli/src/cli.js
CHANGED
|
@@ -1183,6 +1183,34 @@ const SPECS = [
|
|
|
1183
1183
|
"runs have passed. Use `coderook runner` to execute them on this machine.",
|
|
1184
1184
|
run: service_commands_js_1.commandWorkflows,
|
|
1185
1185
|
},
|
|
1186
|
+
{
|
|
1187
|
+
name: "runs",
|
|
1188
|
+
group: "Actions",
|
|
1189
|
+
summary: "what has run, and how it went",
|
|
1190
|
+
usage: "runs [project]",
|
|
1191
|
+
detail: "Newest first, with the machine that took each one. A run that is still\n" +
|
|
1192
|
+
"going shows the runner holding it, which is the first thing worth\n" +
|
|
1193
|
+
"knowing when one is taking longer than it should.",
|
|
1194
|
+
options: [
|
|
1195
|
+
{
|
|
1196
|
+
flags: "--status <state>",
|
|
1197
|
+
description: "queued, running, passed, failed or cancelled",
|
|
1198
|
+
},
|
|
1199
|
+
],
|
|
1200
|
+
examples: ["coderook runs --status failed"],
|
|
1201
|
+
run: service_commands_js_1.commandRuns,
|
|
1202
|
+
},
|
|
1203
|
+
{
|
|
1204
|
+
name: "logs",
|
|
1205
|
+
group: "Actions",
|
|
1206
|
+
summary: "what one run printed",
|
|
1207
|
+
usage: "logs <number> [project]",
|
|
1208
|
+
detail: "The output the runner sent up, in order. Anything the command wrote to\n" +
|
|
1209
|
+
"stderr is shown in red. Exits 1 when the run failed, so this can be\n" +
|
|
1210
|
+
"the last line of a script.",
|
|
1211
|
+
examples: ["coderook logs 12", "coderook logs #12 my-project"],
|
|
1212
|
+
run: service_commands_js_1.commandLogs,
|
|
1213
|
+
},
|
|
1186
1214
|
{
|
|
1187
1215
|
name: "delete",
|
|
1188
1216
|
group: "Your projects",
|
|
@@ -1235,8 +1263,10 @@ const SPECS = [
|
|
|
1235
1263
|
{
|
|
1236
1264
|
name: "merges",
|
|
1237
1265
|
group: "When somebody saved first",
|
|
1238
|
-
summary: "
|
|
1239
|
-
usage: "merges",
|
|
1266
|
+
summary: "this folder's uploads waiting on a decision",
|
|
1267
|
+
usage: "merges [folder]",
|
|
1268
|
+
detail: "Both merge commands read the project this folder is linked to, so run\n" +
|
|
1269
|
+
"them from a working copy — or name one.",
|
|
1240
1270
|
run: commandMerges,
|
|
1241
1271
|
},
|
|
1242
1272
|
{
|
|
@@ -16,6 +16,8 @@ exports.commandReleases = commandReleases;
|
|
|
16
16
|
exports.commandCollaborators = commandCollaborators;
|
|
17
17
|
exports.commandWatch = commandWatch;
|
|
18
18
|
exports.commandWorkflows = commandWorkflows;
|
|
19
|
+
exports.commandRuns = commandRuns;
|
|
20
|
+
exports.commandLogs = commandLogs;
|
|
19
21
|
exports.commandTokens = commandTokens;
|
|
20
22
|
exports.commandDelete = commandDelete;
|
|
21
23
|
const node_process_1 = __importDefault(require("node:process"));
|
|
@@ -182,6 +184,116 @@ async function commandWorkflows(parsed) {
|
|
|
182
184
|
}
|
|
183
185
|
return 0;
|
|
184
186
|
}
|
|
187
|
+
const RUN_STATES = ["queued", "running", "passed", "failed", "cancelled"];
|
|
188
|
+
/** Colour a status the way somebody scanning the list would want it. */
|
|
189
|
+
function statusText(status) {
|
|
190
|
+
if (status === "passed")
|
|
191
|
+
return green(status);
|
|
192
|
+
if (status === "failed" || status === "cancelled")
|
|
193
|
+
return red(status);
|
|
194
|
+
if (status === "running")
|
|
195
|
+
return accent(status);
|
|
196
|
+
return dim(status);
|
|
197
|
+
}
|
|
198
|
+
function elapsed(ms) {
|
|
199
|
+
if (ms === null)
|
|
200
|
+
return "";
|
|
201
|
+
const seconds = Math.round(ms / 1000);
|
|
202
|
+
if (seconds < 60)
|
|
203
|
+
return `${seconds}s`;
|
|
204
|
+
return `${Math.floor(seconds / 60)}m ${String(seconds % 60).padStart(2, "0")}s`;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* What has run, and how it went.
|
|
208
|
+
*
|
|
209
|
+
* The runner has always shipped every line of output to the service, and the
|
|
210
|
+
* service has always kept it — but the only way to read any of it back was
|
|
211
|
+
* the website. Somebody running a job from a terminal had to leave the
|
|
212
|
+
* terminal to find out why it failed.
|
|
213
|
+
*/
|
|
214
|
+
async function commandRuns(parsed) {
|
|
215
|
+
const project = await (0, project_commands_js_1.resolveProject)(parsed.positional[0]);
|
|
216
|
+
if (!project)
|
|
217
|
+
return 1;
|
|
218
|
+
const status = parsed.flags.get("status");
|
|
219
|
+
if (typeof status === "string" && !RUN_STATES.includes(status)) {
|
|
220
|
+
console.error(red(`Status must be one of: ${RUN_STATES.join(", ")}`));
|
|
221
|
+
return 1;
|
|
222
|
+
}
|
|
223
|
+
const found = await (0, api_js_1.runs)(project.id, {
|
|
224
|
+
status: typeof status === "string" ? status : undefined,
|
|
225
|
+
});
|
|
226
|
+
if (!found.length) {
|
|
227
|
+
console.log(dim("Nothing has run on this project."));
|
|
228
|
+
return 0;
|
|
229
|
+
}
|
|
230
|
+
console.log(bold(project.name));
|
|
231
|
+
for (const run of found) {
|
|
232
|
+
const when = run.startedAt
|
|
233
|
+
? new Date(run.startedAt).toLocaleString()
|
|
234
|
+
: dim("not started");
|
|
235
|
+
console.log(` ${accent(`#${run.number}`).padEnd(15)} ${statusText(run.status).padEnd(20)} ` +
|
|
236
|
+
`${run.workflow.padEnd(24)} ${elapsed(run.durationMs).padEnd(9)} ${dim(when)}`);
|
|
237
|
+
const aside = [
|
|
238
|
+
run.version === null ? "" : `v${run.version}`,
|
|
239
|
+
run.claimedBy ?? "",
|
|
240
|
+
run.summary,
|
|
241
|
+
].filter(Boolean);
|
|
242
|
+
if (aside.length)
|
|
243
|
+
console.log(` ${dim(aside.join(" · "))}`);
|
|
244
|
+
}
|
|
245
|
+
console.log("");
|
|
246
|
+
console.log(dim(" Read one with: coderook logs <number>"));
|
|
247
|
+
return 0;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* What one run printed.
|
|
251
|
+
*
|
|
252
|
+
* Taken by the number people actually say — "#12 failed" — rather than the
|
|
253
|
+
* identifier the service uses, which nobody has in front of them.
|
|
254
|
+
*/
|
|
255
|
+
async function commandLogs(parsed) {
|
|
256
|
+
const asked = parsed.positional[0];
|
|
257
|
+
if (!asked) {
|
|
258
|
+
console.error(red("Which run? Try: coderook logs 12"));
|
|
259
|
+
return 1;
|
|
260
|
+
}
|
|
261
|
+
const number = Number(asked.replace(/^#/, ""));
|
|
262
|
+
if (!Number.isInteger(number) || number < 1) {
|
|
263
|
+
console.error(red(`"${asked}" is not a run number.`));
|
|
264
|
+
return 1;
|
|
265
|
+
}
|
|
266
|
+
const project = await (0, project_commands_js_1.resolveProject)(parsed.positional[1]);
|
|
267
|
+
if (!project)
|
|
268
|
+
return 1;
|
|
269
|
+
const found = await (0, api_js_1.runs)(project.id);
|
|
270
|
+
const run = found.find((one) => one.number === number);
|
|
271
|
+
if (!run) {
|
|
272
|
+
console.error(red(`${project.name} has no run #${number}.`));
|
|
273
|
+
if (found.length) {
|
|
274
|
+
console.error(dim(` The most recent is #${found[0]?.number}.`));
|
|
275
|
+
}
|
|
276
|
+
return 1;
|
|
277
|
+
}
|
|
278
|
+
const lines = await (0, api_js_1.runLogs)(project.id, run.id);
|
|
279
|
+
console.log(`${accent(`#${run.number}`)} ${bold(run.workflow)} — ${statusText(run.status)}` +
|
|
280
|
+
`${run.summary ? ` · ${dim(run.summary)}` : ""}`);
|
|
281
|
+
if (!lines.length) {
|
|
282
|
+
/*
|
|
283
|
+
Told apart deliberately. A queued run has printed nothing yet, which is
|
|
284
|
+
normal; a finished run that printed nothing is a workflow whose command
|
|
285
|
+
said nothing, which is worth knowing.
|
|
286
|
+
*/
|
|
287
|
+
console.log(dim(run.status === "queued"
|
|
288
|
+
? " Nothing yet; this run has not been picked up."
|
|
289
|
+
: " This run recorded no output."));
|
|
290
|
+
return 0;
|
|
291
|
+
}
|
|
292
|
+
for (const line of lines) {
|
|
293
|
+
console.log(line.stream === "stderr" ? red(line.line) : line.line);
|
|
294
|
+
}
|
|
295
|
+
return run.status === "failed" ? 1 : 0;
|
|
296
|
+
}
|
|
185
297
|
/** The tokens that can act as this account. */
|
|
186
298
|
async function commandTokens(parsed) {
|
|
187
299
|
const revoke = parsed.flags.get("revoke");
|