@runwisp/ui 0.13.0 → 0.15.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/package.json +39 -47
- package/src/astro/CTABand.astro +19 -12
- package/src/astro/Hero.astro +3 -3
- package/src/astro/SiteFooter.astro +4 -4
- package/src/astro/SiteHeader.astro +4 -4
- package/src/lib/actions/portal.ts +0 -3
- package/src/lib/assets/runwisp-logo.svg +1 -8
- package/src/lib/components/AccordionItem.svelte +4 -10
- package/src/lib/components/Alert.svelte +11 -11
- package/src/lib/components/Avatar.svelte +1 -1
- package/src/lib/components/Badge.svelte +13 -13
- package/src/lib/components/Breadcrumb.svelte +3 -3
- package/src/lib/components/Button.svelte +2 -2
- package/src/lib/components/Card.svelte +4 -4
- package/src/lib/components/Checkbox.svelte +12 -10
- package/src/lib/components/CodeBlock.svelte +7 -13
- package/src/lib/components/CronInput.svelte +12 -15
- package/src/lib/components/DataGrid.svelte +14 -13
- package/src/lib/components/Drawer.svelte +9 -9
- package/src/lib/components/Dropdown.svelte +12 -20
- package/src/lib/components/DurationInput.svelte +10 -14
- package/src/lib/components/EmptyState.svelte +1 -1
- package/src/lib/components/ErrorState.svelte +4 -4
- package/src/lib/components/FeatureCard.svelte +4 -4
- package/src/lib/components/FormField.svelte +4 -6
- package/src/lib/components/Heading.svelte +4 -1
- package/src/lib/components/Input.svelte +10 -12
- package/src/lib/components/Kbd.svelte +1 -1
- package/src/lib/components/Link.svelte +2 -3
- package/src/lib/components/LinkButton.svelte +2 -2
- package/src/lib/components/LogConsole.svelte +22 -21
- package/src/lib/components/Logo.svelte +3 -1
- package/src/lib/components/Modal.svelte +9 -11
- package/src/lib/components/PageContainer.svelte +1 -1
- package/src/lib/components/PageHeader.svelte +1 -1
- package/src/lib/components/Pagination.svelte +4 -4
- package/src/lib/components/Popover.svelte +5 -7
- package/src/lib/components/Progress.svelte +6 -4
- package/src/lib/components/Prose.svelte +4 -4
- package/src/lib/components/Radio.svelte +11 -10
- package/src/lib/components/RadioGroup.svelte +3 -5
- package/src/lib/components/Select.svelte +23 -36
- package/src/lib/components/Separator.svelte +4 -1
- package/src/lib/components/Skeleton.svelte +6 -6
- package/src/lib/components/StatusIndicator.svelte +7 -18
- package/src/lib/components/Table.svelte +1 -1
- package/src/lib/components/TableCell.svelte +1 -1
- package/src/lib/components/TableRow.svelte +1 -5
- package/src/lib/components/Tabs.svelte +10 -13
- package/src/lib/components/TaskCard.svelte +7 -4
- package/src/lib/components/Textarea.svelte +10 -11
- package/src/lib/components/ThemeToggle.svelte +1 -1
- package/src/lib/components/ToastContainer.svelte +3 -8
- package/src/lib/components/Toggle.svelte +2 -3
- package/src/lib/components/Tooltip.svelte +12 -10
- package/src/lib/components/button-styles.ts +17 -18
- package/src/lib/components/dashboard/RunDetailPanel.svelte +160 -249
- package/src/lib/components/dashboard/RunFilterPopover.svelte +31 -25
- package/src/lib/components/dashboard/RunsList.svelte +55 -50
- package/src/lib/components/dashboard/run-filters.test.ts +31 -33
- package/src/lib/components/dashboard/run-filters.ts +33 -33
- package/src/lib/components/dashboard/run-helpers.test.ts +42 -17
- package/src/lib/components/dashboard/run-helpers.ts +53 -17
- package/src/lib/components/dashboard/status-config.ts +9 -8
- package/src/lib/theme-tokens.css +179 -114
- package/src/lib/theme.css +49 -35
- package/src/lib/utils/error.ts +0 -3
- package/src/lib/utils/format.test.ts +2 -6
- package/src/lib/utils/format.ts +28 -3
|
@@ -8,7 +8,7 @@ export type RunsListSortDirection = "asc" | "desc" | "";
|
|
|
8
8
|
/**
|
|
9
9
|
* The single filter shape shared by the runs list, the filter popover, the
|
|
10
10
|
* SSE-merge source, and the bulk selector. Every field beyond `search` and
|
|
11
|
-
* `
|
|
11
|
+
* `sortDirection` is an optional dimension applied server-side; the zero
|
|
12
12
|
* value (`emptyRunFilters`) matches every run.
|
|
13
13
|
*
|
|
14
14
|
* `statuses` is a multi-select set joined to a comma-separated string only at
|
|
@@ -19,26 +19,26 @@ export type RunsListSortDirection = "asc" | "desc" | "";
|
|
|
19
19
|
export interface RunsListFilters {
|
|
20
20
|
search: string;
|
|
21
21
|
statuses: string[];
|
|
22
|
-
|
|
22
|
+
sortDirection: RunsListSortDirection;
|
|
23
23
|
// Optional dimensions explicitly admit `undefined` so a dimension can be
|
|
24
24
|
// cleared by reassignment (`{ ...f, x: undefined }`) under the project's
|
|
25
25
|
// exactOptionalPropertyTypes — a fresh object reference is what re-triggers
|
|
26
26
|
// the parent's fetch effect through several levels of `bind:`.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
taskName?: string | undefined;
|
|
28
|
+
createdAfter?: string | undefined;
|
|
29
|
+
createdBefore?: string | undefined;
|
|
30
|
+
triggeredBy?: string | undefined;
|
|
31
31
|
// A human exit-code expression — a bare code (`137`), a comparison (`>100`,
|
|
32
32
|
// `<=150`), or a space-separated combination (`>100 <150`). Normalized to an
|
|
33
33
|
// inclusive [min, max] range (`exitCodeRange`) at the wire boundary; the
|
|
34
|
-
// server only ever sees `
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
// server only ever sees `exitCodeMin` / `exitCodeMax`.
|
|
35
|
+
exitCode?: string | undefined;
|
|
36
|
+
retriesOnly?: boolean | undefined;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
/** A fresh default filter — no dimensions active, newest-first. */
|
|
40
40
|
export function emptyRunFilters(): RunsListFilters {
|
|
41
|
-
return { search: "", statuses: [],
|
|
41
|
+
return { search: "", statuses: [], sortDirection: "desc" };
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
/**
|
|
@@ -131,14 +131,14 @@ function exactBucket(statuses: string[]): StatusBucket | undefined {
|
|
|
131
131
|
* page the task name is the page scope (injected at fetch time), never a
|
|
132
132
|
* popover-set filter, so it stays absent from those filters and is not counted.
|
|
133
133
|
*/
|
|
134
|
-
export type FilterDimension = "status" | "time" | "task" | "
|
|
134
|
+
export type FilterDimension = "status" | "time" | "task" | "triggeredBy" | "exitCode" | "retries";
|
|
135
135
|
|
|
136
136
|
const DIMENSION_ORDER: FilterDimension[] = [
|
|
137
137
|
"status",
|
|
138
138
|
"time",
|
|
139
139
|
"task",
|
|
140
|
-
"
|
|
141
|
-
"
|
|
140
|
+
"triggeredBy",
|
|
141
|
+
"exitCode",
|
|
142
142
|
"retries",
|
|
143
143
|
];
|
|
144
144
|
|
|
@@ -148,15 +148,15 @@ export function dimensionActive(f: RunsListFilters, dim: FilterDimension): boole
|
|
|
148
148
|
case "status":
|
|
149
149
|
return f.statuses.length > 0;
|
|
150
150
|
case "time":
|
|
151
|
-
return Boolean(f.
|
|
151
|
+
return Boolean(f.createdAfter) || Boolean(f.createdBefore);
|
|
152
152
|
case "task":
|
|
153
|
-
return Boolean(f.
|
|
154
|
-
case "
|
|
155
|
-
return Boolean(f.
|
|
156
|
-
case "
|
|
157
|
-
return exitCodeRangeActive(f.
|
|
153
|
+
return Boolean(f.taskName);
|
|
154
|
+
case "triggeredBy":
|
|
155
|
+
return Boolean(f.triggeredBy);
|
|
156
|
+
case "exitCode":
|
|
157
|
+
return exitCodeRangeActive(f.exitCode);
|
|
158
158
|
case "retries":
|
|
159
|
-
return Boolean(f.
|
|
159
|
+
return Boolean(f.retriesOnly);
|
|
160
160
|
}
|
|
161
161
|
}
|
|
162
162
|
|
|
@@ -176,15 +176,15 @@ export function clearDimension(f: RunsListFilters, dim: FilterDimension): RunsLi
|
|
|
176
176
|
case "status":
|
|
177
177
|
return { ...f, statuses: [] };
|
|
178
178
|
case "time":
|
|
179
|
-
return { ...f,
|
|
179
|
+
return { ...f, createdAfter: undefined, createdBefore: undefined };
|
|
180
180
|
case "task":
|
|
181
|
-
return { ...f,
|
|
182
|
-
case "
|
|
183
|
-
return { ...f,
|
|
184
|
-
case "
|
|
185
|
-
return { ...f,
|
|
181
|
+
return { ...f, taskName: undefined };
|
|
182
|
+
case "triggeredBy":
|
|
183
|
+
return { ...f, triggeredBy: undefined };
|
|
184
|
+
case "exitCode":
|
|
185
|
+
return { ...f, exitCode: undefined };
|
|
186
186
|
case "retries":
|
|
187
|
-
return { ...f,
|
|
187
|
+
return { ...f, retriesOnly: undefined };
|
|
188
188
|
}
|
|
189
189
|
}
|
|
190
190
|
|
|
@@ -196,12 +196,12 @@ export function clearPopoverFilters(f: RunsListFilters): RunsListFilters {
|
|
|
196
196
|
return {
|
|
197
197
|
...f,
|
|
198
198
|
statuses: [],
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
199
|
+
createdAfter: undefined,
|
|
200
|
+
createdBefore: undefined,
|
|
201
|
+
taskName: undefined,
|
|
202
|
+
triggeredBy: undefined,
|
|
203
|
+
exitCode: undefined,
|
|
204
|
+
retriesOnly: undefined,
|
|
205
205
|
};
|
|
206
206
|
}
|
|
207
207
|
|
|
@@ -2,29 +2,54 @@
|
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
4
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
import { RUN_STATUSES } from "@runwisp/common";
|
|
5
6
|
import {
|
|
6
7
|
runDuration,
|
|
7
8
|
runStartDelay,
|
|
9
|
+
runVerdict,
|
|
8
10
|
formatTriggeredByLabel,
|
|
9
11
|
runRetryLabel,
|
|
10
12
|
instanceSuffix,
|
|
11
13
|
} from "./run-helpers.js";
|
|
12
14
|
|
|
15
|
+
describe("runVerdict", () => {
|
|
16
|
+
it("covers every run status", () => {
|
|
17
|
+
for (const status of RUN_STATUSES) {
|
|
18
|
+
expect(runVerdict(status).verb, status).toBeTruthy();
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("phrases a timed outcome so a duration reads after it", () => {
|
|
23
|
+
expect(runVerdict("success")).toEqual({ verb: "succeeded in", timed: true });
|
|
24
|
+
expect(runVerdict("failed")).toEqual({ verb: "failed after", timed: true });
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("marks statuses that never produced a duration as untimed", () => {
|
|
28
|
+
// These end without ever running, so the caller renders the verb alone
|
|
29
|
+
// rather than "skipped after —".
|
|
30
|
+
expect(runVerdict("missed").timed).toBe(false);
|
|
31
|
+
expect(runVerdict("skipped").timed).toBe(false);
|
|
32
|
+
expect(runVerdict("dst_skipped").timed).toBe(false);
|
|
33
|
+
expect(runVerdict("queue_full").timed).toBe(false);
|
|
34
|
+
expect(runVerdict("pending").timed).toBe(false);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
13
38
|
describe("runDuration", () => {
|
|
14
|
-
it("returns undefined when
|
|
39
|
+
it("returns undefined when startAt is not set", () => {
|
|
15
40
|
expect(runDuration({})).toBeUndefined();
|
|
16
41
|
});
|
|
17
42
|
|
|
18
|
-
it("returns formatted duration when both
|
|
43
|
+
it("returns formatted duration when both startAt and endAt are set", () => {
|
|
19
44
|
const start = "2024-06-15T12:00:00.000Z";
|
|
20
45
|
const end = "2024-06-15T12:00:05.000Z";
|
|
21
|
-
expect(runDuration({
|
|
46
|
+
expect(runDuration({ startAt: start, endAt: end })).toBe("5s");
|
|
22
47
|
});
|
|
23
48
|
|
|
24
49
|
it("returns ms duration for sub-second runs", () => {
|
|
25
50
|
const start = "2024-06-15T12:00:00.000Z";
|
|
26
51
|
const end = "2024-06-15T12:00:00.500Z";
|
|
27
|
-
expect(runDuration({
|
|
52
|
+
expect(runDuration({ startAt: start, endAt: end })).toBe("500ms");
|
|
28
53
|
});
|
|
29
54
|
|
|
30
55
|
describe("with fake clock", () => {
|
|
@@ -36,35 +61,35 @@ describe("runDuration", () => {
|
|
|
36
61
|
vi.useRealTimers();
|
|
37
62
|
});
|
|
38
63
|
|
|
39
|
-
it("uses current time when
|
|
40
|
-
expect(runDuration({
|
|
64
|
+
it("uses current time when endAt is not set (run still in progress)", () => {
|
|
65
|
+
expect(runDuration({ startAt: "2024-06-15T12:00:00.000Z" })).toBe("2s");
|
|
41
66
|
});
|
|
42
67
|
});
|
|
43
68
|
|
|
44
69
|
it("counts against an injected now for an in-progress run", () => {
|
|
45
70
|
const start = "2024-06-15T12:00:00.000Z";
|
|
46
71
|
const now = new Date("2024-06-15T12:00:07.000Z").getTime();
|
|
47
|
-
expect(runDuration({
|
|
72
|
+
expect(runDuration({ startAt: start }, now)).toBe("7s");
|
|
48
73
|
});
|
|
49
74
|
|
|
50
75
|
it("ignores the injected now once the run has ended", () => {
|
|
51
76
|
const start = "2024-06-15T12:00:00.000Z";
|
|
52
77
|
const end = "2024-06-15T12:00:05.000Z";
|
|
53
78
|
const now = new Date("2024-06-15T12:01:00.000Z").getTime();
|
|
54
|
-
expect(runDuration({
|
|
79
|
+
expect(runDuration({ startAt: start, endAt: end }, now)).toBe("5s");
|
|
55
80
|
});
|
|
56
81
|
});
|
|
57
82
|
|
|
58
83
|
describe("runStartDelay", () => {
|
|
59
|
-
it("returns undefined when
|
|
60
|
-
expect(runStartDelay({
|
|
84
|
+
it("returns undefined when startAt is not set", () => {
|
|
85
|
+
expect(runStartDelay({ createdAt: "2024-06-15T12:00:00.000Z" })).toBeUndefined();
|
|
61
86
|
});
|
|
62
87
|
|
|
63
88
|
it("returns undefined when the run started within a second of its tick", () => {
|
|
64
89
|
expect(
|
|
65
90
|
runStartDelay({
|
|
66
|
-
|
|
67
|
-
|
|
91
|
+
createdAt: "2024-06-15T12:00:00.000Z",
|
|
92
|
+
startAt: "2024-06-15T12:00:00.300Z",
|
|
68
93
|
}),
|
|
69
94
|
).toBeUndefined();
|
|
70
95
|
});
|
|
@@ -72,8 +97,8 @@ describe("runStartDelay", () => {
|
|
|
72
97
|
it("formats the jitter/queue gap when the run started meaningfully later", () => {
|
|
73
98
|
expect(
|
|
74
99
|
runStartDelay({
|
|
75
|
-
|
|
76
|
-
|
|
100
|
+
createdAt: "2024-06-15T03:00:00.000Z",
|
|
101
|
+
startAt: "2024-06-15T03:07:12.000Z",
|
|
77
102
|
}),
|
|
78
103
|
).toBe("7m 12s");
|
|
79
104
|
});
|
|
@@ -91,15 +116,15 @@ describe("formatTriggeredByLabel", () => {
|
|
|
91
116
|
|
|
92
117
|
describe("runRetryLabel", () => {
|
|
93
118
|
it("returns undefined for a first attempt that is not a retry", () => {
|
|
94
|
-
expect(runRetryLabel({
|
|
119
|
+
expect(runRetryLabel({ retryAttempt: 0 })).toBeUndefined();
|
|
95
120
|
});
|
|
96
121
|
|
|
97
122
|
it("labels a run with a positive attempt number", () => {
|
|
98
|
-
expect(runRetryLabel({
|
|
123
|
+
expect(runRetryLabel({ retryAttempt: 2 })).toBe("retry #2");
|
|
99
124
|
});
|
|
100
125
|
|
|
101
126
|
it("labels a run that points back at the run it re-attempts", () => {
|
|
102
|
-
expect(runRetryLabel({
|
|
127
|
+
expect(runRetryLabel({ retryAttempt: 1, retryOfRunId: "01JABC" })).toBe("retry #1");
|
|
103
128
|
});
|
|
104
129
|
});
|
|
105
130
|
|
|
@@ -1,30 +1,68 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: PoppyCake, s.r.o.
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
|
-
import type { Run } from "@runwisp/common";
|
|
4
|
+
import type { Run, RunStatus } from "@runwisp/common";
|
|
5
5
|
import { formatDuration } from "../../utils/format.js";
|
|
6
6
|
|
|
7
|
+
export interface RunVerdict {
|
|
8
|
+
/** Verb phrase for the outcome, ending in its preposition when `timed`. */
|
|
9
|
+
verb: string;
|
|
10
|
+
/**
|
|
11
|
+
* Whether the phrase expects a duration after it. False for statuses that
|
|
12
|
+
* never produced one (nothing ran, or nothing has yet), so the caller
|
|
13
|
+
* renders the verb alone rather than "skipped after —".
|
|
14
|
+
*/
|
|
15
|
+
timed: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The run's outcome as a verb phrase, so a detail view can state it as one
|
|
20
|
+
* sentence ("succeeded in 933ms") instead of a status badge competing with a
|
|
21
|
+
* separate duration readout for the same glance.
|
|
22
|
+
*/
|
|
23
|
+
const RUN_VERDICTS: Record<RunStatus, RunVerdict> = {
|
|
24
|
+
success: { verb: "succeeded in", timed: true },
|
|
25
|
+
failed: { verb: "failed after", timed: true },
|
|
26
|
+
crashed: { verb: "crashed after", timed: true },
|
|
27
|
+
timeout: { verb: "timed out after", timed: true },
|
|
28
|
+
stopped: { verb: "stopped after", timed: true },
|
|
29
|
+
daemon_stopped: { verb: "cut short after", timed: true },
|
|
30
|
+
log_overflow: { verb: "killed after", timed: true },
|
|
31
|
+
start_failed: { verb: "gave up after", timed: true },
|
|
32
|
+
ended: { verb: "ended after", timed: true },
|
|
33
|
+
running: { verb: "running for", timed: true },
|
|
34
|
+
pending: { verb: "queued", timed: false },
|
|
35
|
+
missed: { verb: "never ran", timed: false },
|
|
36
|
+
skipped: { verb: "skipped", timed: false },
|
|
37
|
+
dst_skipped: { verb: "skipped", timed: false },
|
|
38
|
+
queue_full: { verb: "skipped", timed: false },
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export function runVerdict(status: RunStatus): RunVerdict {
|
|
42
|
+
return RUN_VERDICTS[status];
|
|
43
|
+
}
|
|
44
|
+
|
|
7
45
|
export function runDuration(
|
|
8
|
-
run: Pick<Run, "
|
|
46
|
+
run: Pick<Run, "startAt" | "endAt">,
|
|
9
47
|
now: number = Date.now(),
|
|
10
48
|
): string | undefined {
|
|
11
|
-
if (!run.
|
|
12
|
-
const start = new Date(run.
|
|
13
|
-
const end = run.
|
|
49
|
+
if (!run.startAt) return undefined;
|
|
50
|
+
const start = new Date(run.startAt).getTime();
|
|
51
|
+
const end = run.endAt ? new Date(run.endAt).getTime() : now;
|
|
14
52
|
return formatDuration(end - start);
|
|
15
53
|
}
|
|
16
54
|
|
|
17
55
|
/**
|
|
18
|
-
* Gap between when a run was scheduled (`
|
|
19
|
-
* it actually started (`
|
|
56
|
+
* Gap between when a run was scheduled (`createdAt`, the cron tick) and when
|
|
57
|
+
* it actually started (`startAt`), formatted — or undefined when the two are
|
|
20
58
|
* within a second of each other. This is the visible face of `jitter`: a
|
|
21
59
|
* jittered run is created at its tick but starts later inside the window, and
|
|
22
60
|
* this is by how much. It also surfaces queue-wait, since a queued run is
|
|
23
61
|
* created when it joins the line and started when the line clears.
|
|
24
62
|
*/
|
|
25
|
-
export function runStartDelay(run: Pick<Run, "
|
|
26
|
-
if (!run.
|
|
27
|
-
const delay = new Date(run.
|
|
63
|
+
export function runStartDelay(run: Pick<Run, "createdAt" | "startAt">): string | undefined {
|
|
64
|
+
if (!run.startAt) return undefined;
|
|
65
|
+
const delay = new Date(run.startAt).getTime() - new Date(run.createdAt).getTime();
|
|
28
66
|
if (delay < 1000) return undefined;
|
|
29
67
|
return formatDuration(delay);
|
|
30
68
|
}
|
|
@@ -44,8 +82,8 @@ export function instanceSuffix(instanceIndex: number, instanceCount: number): st
|
|
|
44
82
|
return "";
|
|
45
83
|
}
|
|
46
84
|
|
|
47
|
-
/** Human label for why a run fired (the `
|
|
48
|
-
export function formatTriggeredByLabel(triggeredBy: Run["
|
|
85
|
+
/** Human label for why a run fired (the `triggeredBy` source). */
|
|
86
|
+
export function formatTriggeredByLabel(triggeredBy: Run["triggeredBy"]): string {
|
|
49
87
|
if (triggeredBy === "api") return "API";
|
|
50
88
|
if (triggeredBy === "cron") return "Cron";
|
|
51
89
|
if (triggeredBy === "service") return "Service";
|
|
@@ -58,11 +96,9 @@ export function formatTriggeredByLabel(triggeredBy: Run["triggered_by"]): string
|
|
|
58
96
|
* A run is a retry if it carries a positive attempt number or points back at
|
|
59
97
|
* the run it re-attempts.
|
|
60
98
|
*/
|
|
61
|
-
export function runRetryLabel(
|
|
62
|
-
run
|
|
63
|
-
|
|
64
|
-
if (run.retry_attempt > 0 || run.retry_of_run_id) {
|
|
65
|
-
return `retry #${String(run.retry_attempt)}`;
|
|
99
|
+
export function runRetryLabel(run: Pick<Run, "retryAttempt" | "retryOfRunId">): string | undefined {
|
|
100
|
+
if (run.retryAttempt > 0 || run.retryOfRunId) {
|
|
101
|
+
return `retry #${String(run.retryAttempt)}`;
|
|
66
102
|
}
|
|
67
103
|
return undefined;
|
|
68
104
|
}
|
|
@@ -45,7 +45,7 @@ export const RUN_STATUS_CONFIG: Record<RunStatus, RunStatusConfig> = {
|
|
|
45
45
|
border: "border-success-soft",
|
|
46
46
|
dot: "bg-success-surface",
|
|
47
47
|
badge: "bg-success-soft text-success-soft-text",
|
|
48
|
-
description: "The run finished with exit code 0.",
|
|
48
|
+
description: "The run finished with exit code 0 - everything OK.",
|
|
49
49
|
},
|
|
50
50
|
failed: {
|
|
51
51
|
icon: CircleX,
|
|
@@ -64,7 +64,7 @@ export const RUN_STATUS_CONFIG: Record<RunStatus, RunStatusConfig> = {
|
|
|
64
64
|
dot: "bg-danger-surface",
|
|
65
65
|
badge: "bg-danger-soft text-danger-soft-text",
|
|
66
66
|
description:
|
|
67
|
-
"The process was killed, or the daemon found it still 'running' after a hard crash and marked it crashed
|
|
67
|
+
"The process was killed, or the daemon found it still 'running' after a hard crash and marked it crashed. It was not resumed.",
|
|
68
68
|
},
|
|
69
69
|
stopped: {
|
|
70
70
|
icon: CircleStop,
|
|
@@ -73,7 +73,7 @@ export const RUN_STATUS_CONFIG: Record<RunStatus, RunStatusConfig> = {
|
|
|
73
73
|
border: "border-warning-soft",
|
|
74
74
|
dot: "bg-warning-surface",
|
|
75
75
|
badge: "bg-warning-soft text-warning-soft-text",
|
|
76
|
-
description: "
|
|
76
|
+
description: "A human or an external script manually stopped this run before it finished.",
|
|
77
77
|
},
|
|
78
78
|
timeout: {
|
|
79
79
|
icon: TimerOff,
|
|
@@ -82,7 +82,8 @@ export const RUN_STATUS_CONFIG: Record<RunStatus, RunStatusConfig> = {
|
|
|
82
82
|
border: "border-warning-soft",
|
|
83
83
|
dot: "bg-warning-surface",
|
|
84
84
|
badge: "bg-warning-soft text-warning-soft-text",
|
|
85
|
-
description:
|
|
85
|
+
description:
|
|
86
|
+
"The run exceeded its configured timeout and was terminated. Timeout duration can be changed.",
|
|
86
87
|
},
|
|
87
88
|
skipped: {
|
|
88
89
|
icon: SkipForward,
|
|
@@ -101,7 +102,7 @@ export const RUN_STATUS_CONFIG: Record<RunStatus, RunStatusConfig> = {
|
|
|
101
102
|
border: "border-danger-soft",
|
|
102
103
|
dot: "bg-danger-surface",
|
|
103
104
|
badge: "bg-danger-soft text-danger-soft-text",
|
|
104
|
-
description: "The run hit log_max_size and was handled
|
|
105
|
+
description: "The run hit log_max_size and was handled because of log_on_full.",
|
|
105
106
|
},
|
|
106
107
|
queue_full: {
|
|
107
108
|
icon: SkipForward,
|
|
@@ -150,7 +151,7 @@ export const RUN_STATUS_CONFIG: Record<RunStatus, RunStatusConfig> = {
|
|
|
150
151
|
dot: "bg-danger-surface",
|
|
151
152
|
badge: "bg-danger-soft text-danger-soft-text",
|
|
152
153
|
description:
|
|
153
|
-
"The service kept failing to stay up for healthy_after; after
|
|
154
|
+
"The service kept failing to stay up for healthy_after; after restart_attempts the supervisor gave up (FATAL) and stopped restarting it.",
|
|
154
155
|
},
|
|
155
156
|
pending: {
|
|
156
157
|
icon: Clock,
|
|
@@ -176,6 +177,6 @@ export function getRunStatusConfig(status: RunStatus): RunStatusConfig {
|
|
|
176
177
|
return RUN_STATUS_CONFIG[status];
|
|
177
178
|
}
|
|
178
179
|
|
|
179
|
-
export function runDisplayStatus(run: Pick<Run, "status" | "
|
|
180
|
-
return displayStatus(run.status, run.
|
|
180
|
+
export function runDisplayStatus(run: Pick<Run, "status" | "endReason">): RunStatus {
|
|
181
|
+
return displayStatus(run.status, run.endReason);
|
|
181
182
|
}
|