@intentius/behold 0.29.4 → 0.29.5
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/package.json +1 -1
- package/web/kiosk-runs.js +18 -1
- package/web/kiosk-runs.test.js +15 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intentius/behold",
|
|
3
|
-
"version": "0.29.
|
|
3
|
+
"version": "0.29.5",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "behold — a live control plane on chant. See your whole estate (every substrate in one graph), coloured by drift; act through delegated, gated Ops.",
|
package/web/kiosk-runs.js
CHANGED
|
@@ -159,6 +159,22 @@ export function defaultJob(jobs) {
|
|
|
159
159
|
return started[0] || jobs.find((j) => j.status !== "skipped") || jobs[0] || null;
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
+
/** Forgejo's runner line ending a step that exited 3, the code terragucci holds a wave with. */
|
|
163
|
+
const RUNNER_EXIT_3 = /^\s*⚙️\s*\[runner\]: exitcode '3': failure\s*$/u;
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* A log line as the kiosk shows it under a job: in a job held at the gate,
|
|
167
|
+
* the runner's `exitcode '3': failure` reads as what it is, a hold waiting for
|
|
168
|
+
* an approval, never a failure. `{ text, held }`; any other line, and any line
|
|
169
|
+
* of a job that is not held, is kept as it came.
|
|
170
|
+
*/
|
|
171
|
+
export function logLineWords(jobStatus, line) {
|
|
172
|
+
if (jobStatus === "waiting" && typeof line === "string" && RUNNER_EXIT_3.test(line)) {
|
|
173
|
+
return { text: "[runner]: exit code 3: held at the gate, waiting for an approval", held: true };
|
|
174
|
+
}
|
|
175
|
+
return { text: line, held: false };
|
|
176
|
+
}
|
|
177
|
+
|
|
162
178
|
/** The workflow's own steps of a job: not the runner's set-up or trailer, a post step, or an action's (named by its address). */
|
|
163
179
|
function ownSteps(steps) {
|
|
164
180
|
return steps.map((s, i) => ({ s, i })).filter(({ s, i }) => i > 0 && i < steps.length - 1 && !isPostStep(s.name) && !looksLikeAction(s.name));
|
|
@@ -384,7 +400,8 @@ export function renderRun(host, answer, ui, { now = new Date(), pickJob = () =>
|
|
|
384
400
|
if (s.truncated) pre.appendChild(el("div", "r-log-cut", `… ${s.truncated} earlier lines not kept`));
|
|
385
401
|
s.log.forEach((l, n) => {
|
|
386
402
|
const ln = el("div", "r-log-line");
|
|
387
|
-
|
|
403
|
+
const shown = logLineWords(job.status, l);
|
|
404
|
+
ln.append(el("span", "r-ln", String((s.truncated || 0) + n + 1)), el("span", shown.held ? "r-lt r-wait" : "r-lt", shown.text));
|
|
388
405
|
pre.appendChild(ln);
|
|
389
406
|
});
|
|
390
407
|
if (!s.log.length) pre.appendChild(el("div", "r-log-cut", s.status === "running" ? "waiting for output…" : "no output"));
|
package/web/kiosk-runs.test.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { describe, expect, it } from "vitest";
|
|
4
|
-
import { FOLLOW_LINES, followsLog, logKey, nearEnd, mergeRun, newerOf, agoWords, defaultJob, defaultStep, durationWords, jobOrder, liveRun, placeWords, sidebarRuns, splitSkipped, STATUS_TONE, triggerWords } from "./kiosk-runs.js";
|
|
4
|
+
import { FOLLOW_LINES, followsLog, logKey, nearEnd, mergeRun, newerOf, agoWords, defaultJob, defaultStep, durationWords, jobOrder, liveRun, logLineWords, placeWords, sidebarRuns, splitSkipped, STATUS_TONE, triggerWords } from "./kiosk-runs.js";
|
|
5
5
|
|
|
6
6
|
const FIX = join(import.meta.dirname, "..", "src", "__fixtures__", "terragucci-demo", "runs");
|
|
7
7
|
const run = (id) => JSON.parse(readFileSync(join(FIX, `${id}.json`), "utf8"));
|
|
@@ -21,6 +21,20 @@ describe("the runs, as a CI page lists them (#569)", () => {
|
|
|
21
21
|
expect(STATUS_TONE.waiting).toBe("wait");
|
|
22
22
|
expect(STATUS_TONE.waiting).not.toBe(STATUS_TONE.failure);
|
|
23
23
|
});
|
|
24
|
+
it("shows the runner's exit 3 under a job held at the gate as the hold, not a failure", () => {
|
|
25
|
+
const line = "⚙️ [runner]: exitcode '3': failure";
|
|
26
|
+
expect(logLineWords("waiting", line)).toEqual({ text: "[runner]: exit code 3: held at the gate, waiting for an approval", held: true });
|
|
27
|
+
// The fixture's held job ends its log with that line.
|
|
28
|
+
const held = run(444).jobs.find((j) => j.status === "waiting");
|
|
29
|
+
const last = held.steps.find((s) => s.status === "waiting").log.at(-1);
|
|
30
|
+
expect(logLineWords(held.status, last).held).toBe(true);
|
|
31
|
+
// A real failure keeps its line, whatever the code.
|
|
32
|
+
expect(logLineWords("failure", line)).toEqual({ text: line, held: false });
|
|
33
|
+
expect(logLineWords("failure", "⚙️ [runner]: exitcode '1': failure")).toEqual({ text: "⚙️ [runner]: exitcode '1': failure", held: false });
|
|
34
|
+
// A held job's other lines, and another code, are kept as they came.
|
|
35
|
+
expect(logLineWords("waiting", "⚙️ [runner]: exitcode '1': failure").held).toBe(false);
|
|
36
|
+
expect(logLineWords("waiting", "wave 2 of 3: approval ledger (the default)").held).toBe(false);
|
|
37
|
+
});
|
|
24
38
|
it("follows the newest run still going, pinned first", () => {
|
|
25
39
|
const runs = index().runs;
|
|
26
40
|
expect(liveRun(runs).id).toBe(448);
|