@shipfox/client-workflows 17.0.0 → 19.0.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/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-type.log +0 -1
- package/CHANGELOG.md +23 -0
- package/dist/components/job-detail/job-context-panel.d.ts.map +1 -1
- package/dist/components/job-detail/job-context-panel.js +5 -1
- package/dist/components/job-detail/job-context-panel.js.map +1 -1
- package/dist/components/job-detail/job-detail-view.d.ts.map +1 -1
- package/dist/components/job-detail/job-detail-view.js +34 -27
- package/dist/components/job-detail/job-detail-view.js.map +1 -1
- package/dist/components/job-detail/job-empty-states.d.ts +4 -0
- package/dist/components/job-detail/job-empty-states.d.ts.map +1 -1
- package/dist/components/job-detail/job-empty-states.js +43 -3
- package/dist/components/job-detail/job-empty-states.js.map +1 -1
- package/dist/components/job-detail/step-troubleshooting.js +4 -0
- package/dist/components/job-detail/step-troubleshooting.js.map +1 -1
- package/dist/components/step-list/step-list-model.d.ts.map +1 -1
- package/dist/components/step-list/step-list-model.js +1 -0
- package/dist/components/step-list/step-list-model.js.map +1 -1
- package/dist/components/workflow-run-list/job-status-strip.d.ts.map +1 -1
- package/dist/components/workflow-run-list/job-status-strip.js +20 -7
- package/dist/components/workflow-run-list/job-status-strip.js.map +1 -1
- package/dist/components/workflow-run-view/run-workspace-nav.d.ts.map +1 -1
- package/dist/components/workflow-run-view/run-workspace-nav.js +6 -5
- package/dist/components/workflow-run-view/run-workspace-nav.js.map +1 -1
- package/dist/core/entities/job-execution.d.ts +2 -0
- package/dist/core/entities/job-execution.d.ts.map +1 -1
- package/dist/core/entities/job-execution.js.map +1 -1
- package/dist/core/entities/job.d.ts +6 -3
- package/dist/core/entities/job.d.ts.map +1 -1
- package/dist/core/entities/job.js +8 -1
- package/dist/core/entities/job.js.map +1 -1
- package/dist/core/entities/workflow-run.d.ts +7 -4
- package/dist/core/entities/workflow-run.d.ts.map +1 -1
- package/dist/core/entities/workflow-run.js.map +1 -1
- package/dist/hooks/api/workflow-run-mapper.d.ts.map +1 -1
- package/dist/hooks/api/workflow-run-mapper.js +12 -1
- package/dist/hooks/api/workflow-run-mapper.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/components/job-detail/job-context-panel.tsx +4 -0
- package/src/components/job-detail/job-detail-view.tsx +37 -33
- package/src/components/job-detail/job-empty-states.test.ts +105 -1
- package/src/components/job-detail/job-empty-states.tsx +56 -2
- package/src/components/job-detail/step-troubleshooting.tsx +4 -0
- package/src/components/job-graph/job-node.test.tsx +2 -2
- package/src/components/step-list/step-list-model.ts +1 -0
- package/src/components/workflow-run-list/job-status-strip.tsx +25 -12
- package/src/components/workflow-run-list/workflow-run-list-view.test.tsx +73 -0
- package/src/components/workflow-run-list/workflow-run-list.stories.tsx +31 -0
- package/src/components/workflow-run-view/run-workspace-nav.tsx +13 -6
- package/src/core/entities/job-execution.ts +2 -0
- package/src/core/entities/job-status.test.ts +47 -1
- package/src/core/entities/job.ts +18 -4
- package/src/core/entities/workflow-run-display.test.ts +28 -0
- package/src/core/entities/workflow-run.ts +18 -4
- package/src/core/workflow-run.test.ts +13 -0
- package/src/hooks/api/workflow-run-mapper.ts +19 -1
- package/test/fixtures/workflow-run.ts +32 -12
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -29,6 +29,7 @@ interface JobExecutionFields {
|
|
|
29
29
|
name: string;
|
|
30
30
|
status: JobExecutionStatus;
|
|
31
31
|
statusReason: string | null;
|
|
32
|
+
statusReasonMessage: string | null;
|
|
32
33
|
runner: string[] | null;
|
|
33
34
|
outputs: Record<string, unknown> | null;
|
|
34
35
|
triggerEvents: WorkflowExecutionEvent[];
|
|
@@ -49,6 +50,7 @@ export class JobExecution {
|
|
|
49
50
|
name!: string;
|
|
50
51
|
status!: JobExecutionStatus;
|
|
51
52
|
statusReason!: string | null;
|
|
53
|
+
statusReasonMessage!: string | null;
|
|
52
54
|
runner!: string[] | null;
|
|
53
55
|
outputs!: Record<string, unknown> | null;
|
|
54
56
|
triggerEvents!: WorkflowExecutionEvent[];
|
|
@@ -42,7 +42,7 @@ describe('deriveJobDisplayStatus', () => {
|
|
|
42
42
|
const statuses: Array<[JobExecutionStatus, string[], JobDisplayStatus]> = [
|
|
43
43
|
['pending', [], 'pending'],
|
|
44
44
|
['pending', ['running'], 'running'],
|
|
45
|
-
['running', [], '
|
|
45
|
+
['running', [], 'running'],
|
|
46
46
|
['running', ['running'], 'running'],
|
|
47
47
|
];
|
|
48
48
|
|
|
@@ -91,6 +91,52 @@ describe('deriveJobDisplayStatus', () => {
|
|
|
91
91
|
).toBe('listening');
|
|
92
92
|
});
|
|
93
93
|
|
|
94
|
+
test.each([
|
|
95
|
+
['pending', 'pending'],
|
|
96
|
+
['failed', 'failed'],
|
|
97
|
+
[null, 'pending'],
|
|
98
|
+
] as const)('uses list execution evidence for %s', (executionStatus, expected) => {
|
|
99
|
+
expect(
|
|
100
|
+
deriveJobDisplayStatus({
|
|
101
|
+
mode: 'one_shot',
|
|
102
|
+
status: 'pending',
|
|
103
|
+
listenerStatus: 'inactive',
|
|
104
|
+
executionStatus,
|
|
105
|
+
jobExecutions: [],
|
|
106
|
+
}),
|
|
107
|
+
).toBe(expected);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test('does not infer execution from a running verdict without execution evidence', () => {
|
|
111
|
+
expect(
|
|
112
|
+
deriveJobDisplayStatus({
|
|
113
|
+
mode: 'one_shot',
|
|
114
|
+
status: 'running',
|
|
115
|
+
listenerStatus: 'inactive',
|
|
116
|
+
executionStatus: null,
|
|
117
|
+
jobExecutions: [],
|
|
118
|
+
}),
|
|
119
|
+
).toBe('pending');
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test('uses the same running execution evidence with and without a step tree', () => {
|
|
123
|
+
const detailStatus = deriveJobDisplayStatus({
|
|
124
|
+
mode: 'one_shot',
|
|
125
|
+
status: 'pending',
|
|
126
|
+
listenerStatus: 'inactive',
|
|
127
|
+
jobExecutions: [{status: 'running', steps: [], sequence: 1}] as never,
|
|
128
|
+
});
|
|
129
|
+
const listStatus = deriveJobDisplayStatus({
|
|
130
|
+
mode: 'one_shot',
|
|
131
|
+
status: 'pending',
|
|
132
|
+
listenerStatus: 'inactive',
|
|
133
|
+
executionStatus: 'running',
|
|
134
|
+
jobExecutions: [],
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
expect(listStatus).toBe(detailStatus);
|
|
138
|
+
});
|
|
139
|
+
|
|
94
140
|
test('uses the terminal job status when an active listener has resolved', () => {
|
|
95
141
|
expect(
|
|
96
142
|
deriveJobDisplayStatus({
|
package/src/core/entities/job.ts
CHANGED
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
deriveJobExecutionDisplayStatus,
|
|
3
3
|
type JobExecution,
|
|
4
4
|
type JobExecutionDisplayDuration,
|
|
5
|
+
type JobExecutionStatus,
|
|
5
6
|
} from './job-execution.js';
|
|
6
7
|
import type {EvaluationTraceEntry} from './step-attempt.js';
|
|
7
8
|
|
|
@@ -20,8 +21,10 @@ export type JobStatusReason =
|
|
|
20
21
|
| 'run_cancelled'
|
|
21
22
|
| 'timed_out'
|
|
22
23
|
| 'runner_lost'
|
|
24
|
+
| 'output_too_large'
|
|
23
25
|
| 'step_failed'
|
|
24
|
-
| 'unknown'
|
|
26
|
+
| 'unknown'
|
|
27
|
+
| 'output_invalid';
|
|
25
28
|
export interface JobListening {
|
|
26
29
|
on: Array<{
|
|
27
30
|
source: string;
|
|
@@ -138,12 +141,23 @@ export function isTerminalJobStatus(status: JobStatus): boolean {
|
|
|
138
141
|
}
|
|
139
142
|
|
|
140
143
|
export function deriveJobDisplayStatus(
|
|
141
|
-
job: Pick<Job, 'mode' | 'status' | 'listenerStatus' | 'jobExecutions'
|
|
144
|
+
job: Pick<Job, 'mode' | 'status' | 'listenerStatus' | 'jobExecutions'> & {
|
|
145
|
+
/** List previews carry the selected execution state without its step tree. */
|
|
146
|
+
executionStatus?: JobExecutionStatus | null | undefined;
|
|
147
|
+
},
|
|
142
148
|
): JobDisplayStatus {
|
|
143
149
|
if (isTerminalJobStatus(job.status)) return job.status;
|
|
144
150
|
if (job.mode === 'listening' && job.listenerStatus === 'listening') return 'listening';
|
|
145
|
-
|
|
146
|
-
|
|
151
|
+
const execution =
|
|
152
|
+
job.executionStatus === undefined
|
|
153
|
+
? defaultJobExecution(job)
|
|
154
|
+
: job.executionStatus === null
|
|
155
|
+
? undefined
|
|
156
|
+
: {status: job.executionStatus, steps: []};
|
|
157
|
+
// A running execution is the shared display rule for both list previews and detail jobs.
|
|
158
|
+
// The list has no step tree, while the detail path can still use the execution's steps for
|
|
159
|
+
// other non-running states.
|
|
160
|
+
if (execution?.status === 'running') return 'running';
|
|
147
161
|
return execution ? deriveJobExecutionDisplayStatus(execution) : 'pending';
|
|
148
162
|
}
|
|
149
163
|
|
|
@@ -100,6 +100,34 @@ describe('workflowRunListItemDisplay', () => {
|
|
|
100
100
|
expect(display.status).toBe('running');
|
|
101
101
|
expect(display.duration).toMatchObject({kind: 'run'});
|
|
102
102
|
});
|
|
103
|
+
|
|
104
|
+
test('uses execution-derived running counts for the row headline', () => {
|
|
105
|
+
const run = workflowRunListItem({
|
|
106
|
+
status: 'running',
|
|
107
|
+
...workflowRunJobsFixture(['pending']),
|
|
108
|
+
job_display_status_counts: [{status: 'running', count: 1}],
|
|
109
|
+
started_at: ATTEMPT_STARTED_AT,
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const display = workflowRunListItemDisplay(run);
|
|
113
|
+
|
|
114
|
+
expect(display.status).toBe('running');
|
|
115
|
+
expect(display.duration).toMatchObject({kind: 'run'});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test('uses execution-derived listening counts as started work', () => {
|
|
119
|
+
const run = workflowRunListItem({
|
|
120
|
+
status: 'running',
|
|
121
|
+
...workflowRunJobsFixture(['pending']),
|
|
122
|
+
job_display_status_counts: [{status: 'listening', count: 1}],
|
|
123
|
+
started_at: ATTEMPT_STARTED_AT,
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
const display = workflowRunListItemDisplay(run);
|
|
127
|
+
|
|
128
|
+
expect(display.status).toBe('running');
|
|
129
|
+
expect(display.duration).toMatchObject({kind: 'run'});
|
|
130
|
+
});
|
|
103
131
|
});
|
|
104
132
|
|
|
105
133
|
describe('workflowRunBlockingJob', () => {
|
|
@@ -1,5 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
type Job,
|
|
3
|
+
type JobDisplayStatus,
|
|
4
|
+
type JobMode,
|
|
5
|
+
type JobStatus,
|
|
6
|
+
type ListenerStatus,
|
|
7
|
+
WORKFLOW_JOB_STATUSES,
|
|
8
|
+
} from './job.js';
|
|
9
|
+
import {
|
|
10
|
+
elapsedTimeFromTimestamps,
|
|
11
|
+
type JobExecutionDisplayDuration,
|
|
12
|
+
type JobExecutionStatus,
|
|
13
|
+
} from './job-execution.js';
|
|
3
14
|
import type {WorkflowRunAttempt, WorkflowRunAttemptSummary} from './workflow-run-attempt.js';
|
|
4
15
|
|
|
5
16
|
export type WorkflowRunStatus = 'pending' | 'running' | 'succeeded' | 'failed' | 'cancelled';
|
|
@@ -60,11 +71,14 @@ export interface WorkflowRunJobSummary {
|
|
|
60
71
|
key: string;
|
|
61
72
|
name: string | null;
|
|
62
73
|
status: JobStatus;
|
|
74
|
+
mode: JobMode;
|
|
75
|
+
listenerStatus: ListenerStatus;
|
|
76
|
+
executionStatus: JobExecutionStatus | null;
|
|
63
77
|
position: number;
|
|
64
78
|
}
|
|
65
79
|
|
|
66
80
|
export interface WorkflowRunJobStatusCount {
|
|
67
|
-
status:
|
|
81
|
+
status: JobDisplayStatus;
|
|
68
82
|
count: number;
|
|
69
83
|
}
|
|
70
84
|
|
|
@@ -209,7 +223,7 @@ export interface WorkflowRunProgress {
|
|
|
209
223
|
runStatus: WorkflowRunStatus;
|
|
210
224
|
startedAt: string | null;
|
|
211
225
|
finishedAt: string | null;
|
|
212
|
-
jobStatuses:
|
|
226
|
+
jobStatuses: JobDisplayStatus[];
|
|
213
227
|
firstStartedAt?: string | null | undefined;
|
|
214
228
|
}
|
|
215
229
|
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
workflowRunAttemptDto,
|
|
13
13
|
workflowRunDetailDto,
|
|
14
14
|
workflowRunDto,
|
|
15
|
+
workflowRunJobSummaryDto,
|
|
15
16
|
workflowRunListResponseDto,
|
|
16
17
|
workflowStepAttemptDto,
|
|
17
18
|
workflowStepDto,
|
|
@@ -127,6 +128,18 @@ describe('workflow run model mapping', () => {
|
|
|
127
128
|
});
|
|
128
129
|
});
|
|
129
130
|
|
|
131
|
+
test('keeps legacy raw-status counts aligned with preview glyphs', () => {
|
|
132
|
+
const {job_display_status_counts: _displayCounts, ...legacyDto} = workflowRunDto({
|
|
133
|
+
jobs: [workflowRunJobSummaryDto({status: 'running', execution_status: null})],
|
|
134
|
+
job_status_counts: [{status: 'running', count: 1}],
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
const run = toWorkflowRunListItem(legacyDto);
|
|
138
|
+
|
|
139
|
+
expect(run.jobs.preview[0]?.executionStatus).toBe('running');
|
|
140
|
+
expect(run.jobs.statusCounts).toEqual([{status: 'running', count: 1}]);
|
|
141
|
+
});
|
|
142
|
+
|
|
130
143
|
test('maps run list pagination fields', () => {
|
|
131
144
|
const dto = workflowRunListResponseDto({
|
|
132
145
|
runs: [
|
|
@@ -96,7 +96,10 @@ export function toWorkflowRunRecord(dto: WorkflowRunResponseDto): WorkflowRunRec
|
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
export function toWorkflowRunListItem(dto: WorkflowRunListItemDto): WorkflowRunListItem {
|
|
99
|
-
const
|
|
99
|
+
const hasDisplayStatusCounts = dto.job_display_status_counts !== undefined;
|
|
100
|
+
const statusCounts = (dto.job_display_status_counts ?? dto.job_status_counts).map(
|
|
101
|
+
({status, count}) => ({status, count}),
|
|
102
|
+
);
|
|
100
103
|
return {
|
|
101
104
|
...toWorkflowRunRecord(dto),
|
|
102
105
|
jobs: {
|
|
@@ -105,6 +108,14 @@ export function toWorkflowRunListItem(dto: WorkflowRunListItemDto): WorkflowRunL
|
|
|
105
108
|
key: job.key,
|
|
106
109
|
name: job.name,
|
|
107
110
|
status: job.status,
|
|
111
|
+
mode: job.mode ?? 'one_shot',
|
|
112
|
+
listenerStatus: job.listener_status ?? 'inactive',
|
|
113
|
+
// The optional display-count field is the rollout capability signal. Pre-display API
|
|
114
|
+
// responses only carry raw verdict counts, so mirror their non-terminal verdict into
|
|
115
|
+
// execution evidence to keep each legacy glyph aligned with those fallback counts.
|
|
116
|
+
executionStatus: hasDisplayStatusCounts
|
|
117
|
+
? (job.execution_status ?? null)
|
|
118
|
+
: legacyExecutionStatus(job.status),
|
|
108
119
|
position: job.position,
|
|
109
120
|
})),
|
|
110
121
|
statusCounts,
|
|
@@ -115,6 +126,12 @@ export function toWorkflowRunListItem(dto: WorkflowRunListItemDto): WorkflowRunL
|
|
|
115
126
|
};
|
|
116
127
|
}
|
|
117
128
|
|
|
129
|
+
function legacyExecutionStatus(
|
|
130
|
+
status: WorkflowRunListItemDto['jobs'][number]['status'],
|
|
131
|
+
): 'pending' | 'running' | null {
|
|
132
|
+
return status === 'pending' || status === 'running' ? status : null;
|
|
133
|
+
}
|
|
134
|
+
|
|
118
135
|
export function toWorkflowRunListPage(dto: WorkflowRunListResponseDto): WorkflowRunListPage {
|
|
119
136
|
return {
|
|
120
137
|
runs: dto.runs.map(toWorkflowRunListItem),
|
|
@@ -165,6 +182,7 @@ export function toJobExecution(dto: WorkflowRunJobExecutionDetailDto): JobExecut
|
|
|
165
182
|
name: dto.name,
|
|
166
183
|
status: dto.status,
|
|
167
184
|
statusReason: dto.status_reason,
|
|
185
|
+
statusReasonMessage: dto.status_reason_message ?? null,
|
|
168
186
|
runner: dto.runner ?? null,
|
|
169
187
|
outputs: dto.outputs ?? null,
|
|
170
188
|
triggerEvents: (dto.trigger_events ?? []).map(toWorkflowExecutionEvent),
|
|
@@ -81,6 +81,7 @@ export function workflowRunDto(
|
|
|
81
81
|
finished_at: null,
|
|
82
82
|
jobs: [],
|
|
83
83
|
job_status_counts: [],
|
|
84
|
+
job_display_status_counts: [],
|
|
84
85
|
...overrides,
|
|
85
86
|
};
|
|
86
87
|
}
|
|
@@ -89,11 +90,15 @@ export function workflowRunJobSummaryDto(
|
|
|
89
90
|
overrides: Partial<WorkflowRunJobSummaryDto> = {},
|
|
90
91
|
): WorkflowRunJobSummaryDto {
|
|
91
92
|
jobSummarySequence += 1;
|
|
93
|
+
const status = overrides.status ?? 'succeeded';
|
|
92
94
|
return {
|
|
93
95
|
id: `${JOB_SUMMARY_ID_PREFIX}${String(jobSummarySequence).padStart(11, '0')}`,
|
|
94
96
|
key: `job-${jobSummarySequence}`,
|
|
95
97
|
name: null,
|
|
96
|
-
status
|
|
98
|
+
status,
|
|
99
|
+
mode: 'one_shot',
|
|
100
|
+
listener_status: 'inactive',
|
|
101
|
+
execution_status: executionStatusForFixtureStatus(status),
|
|
97
102
|
position: jobSummarySequence - 1,
|
|
98
103
|
...overrides,
|
|
99
104
|
};
|
|
@@ -104,13 +109,25 @@ export function workflowRunJobSummaryDtos(
|
|
|
104
109
|
count: number,
|
|
105
110
|
statuses: readonly JobStatusDto[] = [],
|
|
106
111
|
): WorkflowRunJobSummaryDto[] {
|
|
107
|
-
return Array.from({length: count}, (_, index) =>
|
|
108
|
-
|
|
112
|
+
return Array.from({length: count}, (_, index) => {
|
|
113
|
+
const status = statuses[index];
|
|
114
|
+
return workflowRunJobSummaryDto({
|
|
109
115
|
key: `job-${index + 1}`,
|
|
110
116
|
position: index,
|
|
111
|
-
...(
|
|
112
|
-
|
|
113
|
-
|
|
117
|
+
...(status
|
|
118
|
+
? {
|
|
119
|
+
status,
|
|
120
|
+
execution_status: executionStatusForFixtureStatus(status),
|
|
121
|
+
}
|
|
122
|
+
: {}),
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function executionStatusForFixtureStatus(
|
|
128
|
+
status: JobStatusDto,
|
|
129
|
+
): WorkflowRunJobSummaryDto['execution_status'] {
|
|
130
|
+
return status === 'pending' || status === 'running' ? status : null;
|
|
114
131
|
}
|
|
115
132
|
|
|
116
133
|
/**
|
|
@@ -122,16 +139,19 @@ export function workflowRunJobSummaryDtos(
|
|
|
122
139
|
*/
|
|
123
140
|
export function workflowRunJobsFixture(
|
|
124
141
|
statuses: readonly JobStatusDto[],
|
|
125
|
-
): Pick<WorkflowRunListItemDto, 'jobs' | 'job_status_counts'> {
|
|
142
|
+
): Pick<WorkflowRunListItemDto, 'jobs' | 'job_status_counts' | 'job_display_status_counts'> {
|
|
126
143
|
const counts = new Map<JobStatusDto, number>();
|
|
127
144
|
for (const status of statuses) counts.set(status, (counts.get(status) ?? 0) + 1);
|
|
128
145
|
|
|
146
|
+
const preview = workflowRunJobSummaryDtos(
|
|
147
|
+
Math.min(statuses.length, WORKFLOW_RUN_JOB_PREVIEW_LIMIT),
|
|
148
|
+
statuses,
|
|
149
|
+
);
|
|
150
|
+
|
|
129
151
|
return {
|
|
130
|
-
jobs:
|
|
131
|
-
Math.min(statuses.length, WORKFLOW_RUN_JOB_PREVIEW_LIMIT),
|
|
132
|
-
statuses,
|
|
133
|
-
),
|
|
152
|
+
jobs: preview,
|
|
134
153
|
job_status_counts: [...counts.entries()].map(([status, count]) => ({status, count})),
|
|
154
|
+
job_display_status_counts: [...counts.entries()].map(([status, count]) => ({status, count})),
|
|
135
155
|
};
|
|
136
156
|
}
|
|
137
157
|
|
|
@@ -139,7 +159,7 @@ export function workflowRunJobsFixture(
|
|
|
139
159
|
export function workflowRunJobsOfStatus(
|
|
140
160
|
count: number,
|
|
141
161
|
status: JobStatusDto = 'succeeded',
|
|
142
|
-
): Pick<WorkflowRunListItemDto, 'jobs' | 'job_status_counts'> {
|
|
162
|
+
): Pick<WorkflowRunListItemDto, 'jobs' | 'job_status_counts' | 'job_display_status_counts'> {
|
|
143
163
|
return workflowRunJobsFixture(Array.from({length: count}, () => status));
|
|
144
164
|
}
|
|
145
165
|
|