@shipfox/client-workflows 5.0.0 → 6.0.2
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/CHANGELOG.md +68 -0
- package/dist/components/workflow-run-list/types.d.ts +12 -0
- package/dist/components/workflow-run-list/types.d.ts.map +1 -1
- package/dist/components/workflow-run-list/types.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-view.d.ts +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-view.d.ts.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-view.js +32 -11
- package/dist/components/workflow-run-list/workflow-run-list-view.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list.d.ts +1 -1
- package/dist/components/workflow-run-list/workflow-run-list.d.ts.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list.js +7 -2
- package/dist/components/workflow-run-list/workflow-run-list.js.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-summary.d.ts +2 -3
- package/dist/components/workflow-run-summary/workflow-run-summary.d.ts.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -1
- package/dist/components/workflow-run-view/job-card.d.ts.map +1 -1
- package/dist/components/workflow-run-view/job-card.js +9 -8
- package/dist/components/workflow-run-view/job-card.js.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-view.js.map +1 -1
- package/dist/core/entities/job-execution.d.ts +11 -6
- package/dist/core/entities/job-execution.d.ts.map +1 -1
- package/dist/core/entities/job-execution.js +0 -19
- package/dist/core/entities/job-execution.js.map +1 -1
- package/dist/core/entities/job.d.ts +32 -10
- package/dist/core/entities/job.d.ts.map +1 -1
- package/dist/core/entities/job.js +0 -22
- package/dist/core/entities/job.js.map +1 -1
- package/dist/core/entities/step-attempt.d.ts +28 -3
- package/dist/core/entities/step-attempt.d.ts.map +1 -1
- package/dist/core/entities/step-attempt.js +0 -17
- package/dist/core/entities/step-attempt.js.map +1 -1
- package/dist/core/entities/step.d.ts +6 -6
- package/dist/core/entities/step.d.ts.map +1 -1
- package/dist/core/entities/step.js +19 -49
- package/dist/core/entities/step.js.map +1 -1
- package/dist/core/entities/workflow-run-attempt.d.ts +0 -2
- package/dist/core/entities/workflow-run-attempt.d.ts.map +1 -1
- package/dist/core/entities/workflow-run-attempt.js +0 -12
- package/dist/core/entities/workflow-run-attempt.js.map +1 -1
- package/dist/core/entities/workflow-run.d.ts +6 -7
- package/dist/core/entities/workflow-run.d.ts.map +1 -1
- package/dist/core/entities/workflow-run.js +1 -67
- package/dist/core/entities/workflow-run.js.map +1 -1
- package/dist/core/workflow-run.d.ts +9 -9
- package/dist/core/workflow-run.d.ts.map +1 -1
- package/dist/core/workflow-run.js +6 -6
- package/dist/core/workflow-run.js.map +1 -1
- package/dist/feature.d.ts +13 -0
- package/dist/feature.d.ts.map +1 -1
- package/dist/feature.js +17 -16
- package/dist/feature.js.map +1 -1
- package/dist/hooks/api/workflow-run-mapper.d.ts +12 -0
- package/dist/hooks/api/workflow-run-mapper.d.ts.map +1 -0
- package/dist/hooks/api/workflow-run-mapper.js +207 -0
- package/dist/hooks/api/workflow-run-mapper.js.map +1 -0
- package/dist/hooks/api/workflow-runs.d.ts +25 -55
- package/dist/hooks/api/workflow-runs.d.ts.map +1 -1
- package/dist/hooks/api/workflow-runs.js +95 -51
- package/dist/hooks/api/workflow-runs.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/pages/project-workflows-page.d.ts.map +1 -1
- package/dist/pages/project-workflows-page.js +14 -14
- package/dist/pages/project-workflows-page.js.map +1 -1
- package/dist/pages/workflow-run-page.d.ts +3 -1
- package/dist/pages/workflow-run-page.d.ts.map +1 -1
- package/dist/pages/workflow-run-page.js +21 -14
- package/dist/pages/workflow-run-page.js.map +1 -1
- package/dist/routes/inputs.d.ts +22 -0
- package/dist/routes/inputs.d.ts.map +1 -0
- package/dist/routes/inputs.js +76 -0
- package/dist/routes/inputs.js.map +1 -0
- package/dist/routes/run-detail.d.ts +2 -0
- package/dist/routes/run-detail.d.ts.map +1 -1
- package/dist/routes/run-detail.js +6 -6
- package/dist/routes/run-detail.js.map +1 -1
- package/dist/routes/runs.d.ts +2 -0
- package/dist/routes/runs.d.ts.map +1 -1
- package/dist/routes/runs.js +6 -6
- package/dist/routes/runs.js.map +1 -1
- package/dist/routes/workflows.js +3 -5
- package/dist/routes/workflows.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +13 -14
- package/src/components/workflow-run-list/types.ts +6 -0
- package/src/components/workflow-run-list/workflow-run-list-view.tsx +27 -11
- package/src/components/workflow-run-list/workflow-run-list.tsx +6 -0
- package/src/components/workflow-run-summary/workflow-run-attempt-switcher.test.tsx +5 -6
- package/src/components/workflow-run-summary/workflow-run-summary.tsx +3 -3
- package/src/components/workflow-run-view/job-card.tsx +20 -11
- package/src/components/workflow-run-view/workflow-run-view.test.tsx +3 -3
- package/src/components/workflow-run-view/workflow-run-view.tsx +2 -2
- package/src/components/workflow-source-panel/workflow-source-panel.stories.tsx +4 -0
- package/src/core/entities/job-execution.ts +12 -28
- package/src/core/entities/job.ts +44 -45
- package/src/core/entities/step-attempt.ts +9 -20
- package/src/core/entities/step.ts +38 -66
- package/src/core/entities/workflow-run-attempt.ts +0 -14
- package/src/core/entities/workflow-run.ts +8 -86
- package/src/core/workflow-run.test.ts +13 -11
- package/src/core/workflow-run.ts +8 -13
- package/src/feature.ts +19 -17
- package/src/hooks/api/workflow-run-mapper.ts +246 -0
- package/src/hooks/api/workflow-runs.test.tsx +57 -59
- package/src/hooks/api/workflow-runs.ts +187 -88
- package/src/index.ts +4 -0
- package/src/pages/project-workflows-page.test.tsx +1 -1
- package/src/pages/project-workflows-page.tsx +31 -26
- package/src/pages/workflow-run-page.test.tsx +4 -2
- package/src/pages/workflow-run-page.tsx +22 -17
- package/src/routes/inputs.test.ts +22 -0
- package/src/routes/inputs.ts +58 -0
- package/src/routes/run-detail.tsx +12 -8
- package/src/routes/runs.tsx +11 -4
- package/src/routes/workflows.tsx +3 -3
- package/test/fixtures/workflow-run.ts +9 -7
- package/test/pages.tsx +20 -5
- package/tsconfig.build.tsbuildinfo +1 -1
- package/vercel.json +5 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shipfox/client-workflows",
|
|
3
3
|
"license": "MIT",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "6.0.2",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/ShipfoxHQ/shipfox.git",
|
|
@@ -10,10 +10,6 @@
|
|
|
10
10
|
"type": "module",
|
|
11
11
|
"main": "dist/index.js",
|
|
12
12
|
"types": "dist/index.d.ts",
|
|
13
|
-
"imports": {
|
|
14
|
-
"#*": "./dist/*",
|
|
15
|
-
"#test/*": "./test/*"
|
|
16
|
-
},
|
|
17
13
|
"exports": {
|
|
18
14
|
".": {
|
|
19
15
|
"types": "./dist/index.d.ts",
|
|
@@ -31,15 +27,15 @@
|
|
|
31
27
|
"dependencies": {
|
|
32
28
|
"@swc/helpers": "^0.5.17",
|
|
33
29
|
"date-fns": "^4.1.0",
|
|
34
|
-
"@shipfox/api-definitions-dto": "
|
|
35
|
-
"@shipfox/api-workflows-dto": "
|
|
36
|
-
"@shipfox/client-api": "
|
|
37
|
-
"@shipfox/client-
|
|
38
|
-
"@shipfox/client-
|
|
39
|
-
"@shipfox/client-shell": "
|
|
40
|
-
"@shipfox/
|
|
41
|
-
"@shipfox/client-ui": "
|
|
42
|
-
"@shipfox/
|
|
30
|
+
"@shipfox/api-definitions-dto": "9.0.2",
|
|
31
|
+
"@shipfox/api-workflows-dto": "9.0.2",
|
|
32
|
+
"@shipfox/client-api": "6.0.1",
|
|
33
|
+
"@shipfox/client-logs": "6.0.2",
|
|
34
|
+
"@shipfox/client-projects": "6.0.2",
|
|
35
|
+
"@shipfox/client-shell": "6.0.2",
|
|
36
|
+
"@shipfox/react-ui": "0.3.7",
|
|
37
|
+
"@shipfox/client-ui": "6.0.2",
|
|
38
|
+
"@shipfox/client-triggers": "6.0.2"
|
|
43
39
|
},
|
|
44
40
|
"peerDependencies": {
|
|
45
41
|
"@tanstack/react-query": "^5.101.0",
|
|
@@ -47,6 +43,9 @@
|
|
|
47
43
|
"react": "^19.0.0",
|
|
48
44
|
"react-dom": "^19.0.0"
|
|
49
45
|
},
|
|
46
|
+
"imports": {
|
|
47
|
+
"#*": "./dist/*"
|
|
48
|
+
},
|
|
50
49
|
"scripts": {
|
|
51
50
|
"build": "shipfox-swc",
|
|
52
51
|
"check": "shipfox-biome-check",
|
|
@@ -8,6 +8,9 @@ export interface WorkflowRunListProps {
|
|
|
8
8
|
projectId: string;
|
|
9
9
|
selectedWorkflowRunId?: string | undefined;
|
|
10
10
|
className?: string | undefined;
|
|
11
|
+
search?: string;
|
|
12
|
+
statusFilter?: WorkflowRunListStatusFilter;
|
|
13
|
+
onFiltersChange?: (filters: {search?: string; status?: WorkflowRunListStatusFilter}) => void;
|
|
11
14
|
}
|
|
12
15
|
|
|
13
16
|
export type WorkflowRunListQuery = QueryLoadErrorQuery & {isPending: boolean};
|
|
@@ -19,4 +22,7 @@ export interface WorkflowRunListViewProps {
|
|
|
19
22
|
projectId: string;
|
|
20
23
|
selectedWorkflowRunId?: string | undefined;
|
|
21
24
|
className?: string | undefined;
|
|
25
|
+
search?: string;
|
|
26
|
+
statusFilter?: WorkflowRunListStatusFilter;
|
|
27
|
+
onFiltersChange?: (filters: {search?: string; status?: WorkflowRunListStatusFilter}) => void;
|
|
22
28
|
}
|
|
@@ -2,7 +2,7 @@ import {TimeTickerProvider} from '@shipfox/react-ui/time-ticker';
|
|
|
2
2
|
import {cn} from '@shipfox/react-ui/utils';
|
|
3
3
|
import {useState} from 'react';
|
|
4
4
|
import {runMatchesSearch, runMatchesStatusFilter} from './run-display.js';
|
|
5
|
-
import type {
|
|
5
|
+
import type {WorkflowRunListViewProps} from './types.js';
|
|
6
6
|
import {WorkflowRunListContent} from './workflow-run-list-content.js';
|
|
7
7
|
import {WorkflowRunListHeader} from './workflow-run-list-header.js';
|
|
8
8
|
|
|
@@ -13,18 +13,26 @@ export function WorkflowRunListView({
|
|
|
13
13
|
projectId,
|
|
14
14
|
selectedWorkflowRunId,
|
|
15
15
|
className,
|
|
16
|
+
search = '',
|
|
17
|
+
statusFilter = 'all',
|
|
18
|
+
onFiltersChange,
|
|
16
19
|
}: WorkflowRunListViewProps) {
|
|
17
|
-
const [
|
|
18
|
-
const [
|
|
20
|
+
const [localSearch, setLocalSearch] = useState(search);
|
|
21
|
+
const [localStatusFilter, setLocalStatusFilter] = useState(statusFilter);
|
|
22
|
+
const currentSearch = onFiltersChange ? search : localSearch;
|
|
23
|
+
const currentStatusFilter = onFiltersChange ? statusFilter : localStatusFilter;
|
|
19
24
|
|
|
20
25
|
const filteredRuns = runs.filter((run) => {
|
|
21
|
-
if (!runMatchesStatusFilter(run.status,
|
|
22
|
-
return runMatchesSearch(run,
|
|
26
|
+
if (!runMatchesStatusFilter(run.status, currentStatusFilter)) return false;
|
|
27
|
+
return runMatchesSearch(run, currentSearch);
|
|
23
28
|
});
|
|
24
29
|
|
|
25
30
|
function handleClearFilters() {
|
|
26
|
-
|
|
27
|
-
|
|
31
|
+
if (onFiltersChange) onFiltersChange({});
|
|
32
|
+
else {
|
|
33
|
+
setLocalSearch('');
|
|
34
|
+
setLocalStatusFilter('all');
|
|
35
|
+
}
|
|
28
36
|
}
|
|
29
37
|
|
|
30
38
|
return (
|
|
@@ -37,10 +45,18 @@ export function WorkflowRunListView({
|
|
|
37
45
|
aria-label="Workflow runs"
|
|
38
46
|
>
|
|
39
47
|
<WorkflowRunListHeader
|
|
40
|
-
query={
|
|
41
|
-
onQueryChange={
|
|
42
|
-
|
|
43
|
-
|
|
48
|
+
query={currentSearch}
|
|
49
|
+
onQueryChange={(next) => {
|
|
50
|
+
if (onFiltersChange)
|
|
51
|
+
onFiltersChange({...(next ? {search: next} : {}), status: currentStatusFilter});
|
|
52
|
+
else setLocalSearch(next);
|
|
53
|
+
}}
|
|
54
|
+
statusFilter={currentStatusFilter}
|
|
55
|
+
onStatusFilterChange={(next) => {
|
|
56
|
+
if (onFiltersChange)
|
|
57
|
+
onFiltersChange({...(currentSearch ? {search: currentSearch} : {}), status: next});
|
|
58
|
+
else setLocalStatusFilter(next);
|
|
59
|
+
}}
|
|
44
60
|
/>
|
|
45
61
|
<WorkflowRunListContent
|
|
46
62
|
query={query}
|
|
@@ -9,6 +9,9 @@ export function WorkflowRunList({
|
|
|
9
9
|
projectId,
|
|
10
10
|
selectedWorkflowRunId,
|
|
11
11
|
className,
|
|
12
|
+
search = '',
|
|
13
|
+
statusFilter = 'all',
|
|
14
|
+
onFiltersChange,
|
|
12
15
|
}: WorkflowRunListProps) {
|
|
13
16
|
const query = useWorkflowRunsInfiniteQuery(projectId, {});
|
|
14
17
|
const runs = useMemo<WorkflowRunListItem[]>(
|
|
@@ -24,6 +27,9 @@ export function WorkflowRunList({
|
|
|
24
27
|
projectId={projectId}
|
|
25
28
|
selectedWorkflowRunId={selectedWorkflowRunId}
|
|
26
29
|
className={className}
|
|
30
|
+
search={search}
|
|
31
|
+
statusFilter={statusFilter}
|
|
32
|
+
{...(onFiltersChange ? {onFiltersChange} : {})}
|
|
27
33
|
/>
|
|
28
34
|
);
|
|
29
35
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {configureApiClient} from '@shipfox/client-api';
|
|
2
2
|
import {screen, waitFor, within} from '@testing-library/react';
|
|
3
3
|
import userEvent from '@testing-library/user-event';
|
|
4
|
+
import {toWorkflowRunAttempt} from '#hooks/api/workflow-run-mapper.js';
|
|
4
5
|
import {workflowRunsQueryKeys} from '#hooks/api/workflow-runs.js';
|
|
5
6
|
import {
|
|
6
7
|
runAttemptsResponseDto,
|
|
@@ -145,12 +146,10 @@ describe('WorkflowRunAttemptSwitcher', () => {
|
|
|
145
146
|
const {queryClient} = renderSwitcher({latestAttempt: 3});
|
|
146
147
|
queryClient.setQueryData(
|
|
147
148
|
workflowRunsQueryKeys.attempts(CURRENT_RUN_ID),
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
],
|
|
153
|
-
}),
|
|
149
|
+
[
|
|
150
|
+
workflowRunAttemptDto({id: ROOT_RUN_ID, attempt: 1}),
|
|
151
|
+
workflowRunAttemptDto({id: CURRENT_RUN_ID, attempt: 2}),
|
|
152
|
+
].map(toWorkflowRunAttempt),
|
|
154
153
|
);
|
|
155
154
|
|
|
156
155
|
await user.click(await screen.findByRole('button', {name: 'Switch attempt, currently 2 of 3'}));
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type {WorkflowRunRerunModeDto} from '@shipfox/api-workflows-dto';
|
|
2
1
|
import {TriggerSourceIcon} from '@shipfox/client-triggers';
|
|
3
2
|
import {Badge} from '@shipfox/react-ui/badge';
|
|
4
3
|
import {Button} from '@shipfox/react-ui/button';
|
|
@@ -20,6 +19,7 @@ import {
|
|
|
20
19
|
type Job,
|
|
21
20
|
WORKFLOW_RUN_STATUSES,
|
|
22
21
|
type WorkflowRunDetail,
|
|
22
|
+
type WorkflowRunRerunMode,
|
|
23
23
|
} from '#core/workflow-run.js';
|
|
24
24
|
import {WorkflowRunDurationLabel} from '../workflow-run-duration-label.js';
|
|
25
25
|
import {getWorkflowStatusVisual} from '../workflow-status/status-visuals.js';
|
|
@@ -43,7 +43,7 @@ export interface WorkflowRunSummaryProps {
|
|
|
43
43
|
cancelling?: boolean | undefined;
|
|
44
44
|
onCancel?: (() => void) | undefined;
|
|
45
45
|
rerunPending?: boolean | undefined;
|
|
46
|
-
onRerun?: ((mode:
|
|
46
|
+
onRerun?: ((mode: WorkflowRunRerunMode) => void) | undefined;
|
|
47
47
|
latestAttempt?: number | undefined;
|
|
48
48
|
}
|
|
49
49
|
|
|
@@ -207,7 +207,7 @@ function WorkflowRunActionSlot({
|
|
|
207
207
|
cancelling: boolean;
|
|
208
208
|
onCancel?: (() => void) | undefined;
|
|
209
209
|
rerunPending: boolean;
|
|
210
|
-
onRerun?: ((mode:
|
|
210
|
+
onRerun?: ((mode: WorkflowRunRerunMode) => void) | undefined;
|
|
211
211
|
}) {
|
|
212
212
|
if (action === 'none') return null;
|
|
213
213
|
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import {agentConfigIssueSchema, stepErrorReasonSchema} from '@shipfox/api-workflows-dto';
|
|
2
1
|
import {TriggerSourceIcon} from '@shipfox/client-triggers';
|
|
3
2
|
import {Badge} from '@shipfox/react-ui/badge';
|
|
4
3
|
import {Button} from '@shipfox/react-ui/button';
|
|
@@ -12,9 +11,11 @@ import type {ReactNode} from 'react';
|
|
|
12
11
|
import {Fragment, useId, useRef, useState} from 'react';
|
|
13
12
|
import {getWorkflowStatusVisual} from '#components/workflow-status/status-visuals.js';
|
|
14
13
|
import {
|
|
14
|
+
AGENT_CONFIG_ISSUES,
|
|
15
15
|
type Job,
|
|
16
16
|
type JobExecution,
|
|
17
17
|
type JobExecutionTime,
|
|
18
|
+
STEP_ERROR_REASONS,
|
|
18
19
|
type Step,
|
|
19
20
|
type StepError,
|
|
20
21
|
type StepSourceLocation,
|
|
@@ -404,25 +405,33 @@ function toSelectedAttemptError(
|
|
|
404
405
|
): StepError | null {
|
|
405
406
|
if (error === null) return null;
|
|
406
407
|
|
|
407
|
-
const
|
|
408
|
-
const
|
|
409
|
-
|
|
410
|
-
|
|
408
|
+
const rawReason = error.reason;
|
|
409
|
+
const parsedReason =
|
|
410
|
+
typeof rawReason === 'string' &&
|
|
411
|
+
STEP_ERROR_REASONS.has(rawReason as StepError['reason'] & string)
|
|
412
|
+
? (rawReason as NonNullable<StepError['reason']>)
|
|
413
|
+
: undefined;
|
|
414
|
+
const rawAgentConfigIssue = error.agentConfigIssue ?? error.agent_config_issue;
|
|
415
|
+
const agentConfigIssue =
|
|
416
|
+
typeof rawAgentConfigIssue === 'string' &&
|
|
417
|
+
AGENT_CONFIG_ISSUES.has(rawAgentConfigIssue as NonNullable<StepError['agentConfigIssue']>)
|
|
418
|
+
? (rawAgentConfigIssue as NonNullable<StepError['agentConfigIssue']>)
|
|
419
|
+
: undefined;
|
|
411
420
|
const exitCode = error.exitCode ?? error.exit_code;
|
|
412
|
-
const
|
|
413
|
-
?
|
|
414
|
-
: agentConfigIssue
|
|
421
|
+
const resolvedReason = parsedReason
|
|
422
|
+
? parsedReason
|
|
423
|
+
: agentConfigIssue
|
|
415
424
|
? 'agent_config_invalid'
|
|
416
425
|
: undefined;
|
|
417
426
|
|
|
418
|
-
if (
|
|
427
|
+
if (resolvedReason === undefined) return null;
|
|
419
428
|
|
|
420
429
|
return {
|
|
421
430
|
message: typeof error.message === 'string' ? error.message : '',
|
|
422
431
|
exitCode: exitCode === null || typeof exitCode === 'number' ? exitCode : null,
|
|
423
432
|
signal: typeof error.signal === 'string' ? error.signal : undefined,
|
|
424
|
-
reason:
|
|
425
|
-
agentConfigIssue
|
|
433
|
+
reason: resolvedReason,
|
|
434
|
+
agentConfigIssue,
|
|
426
435
|
category: step.type === 'setup' ? 'setup' : 'user',
|
|
427
436
|
};
|
|
428
437
|
}
|
|
@@ -122,7 +122,7 @@ describe('WorkflowRunView', () => {
|
|
|
122
122
|
listener_status: 'listening',
|
|
123
123
|
job_executions: [
|
|
124
124
|
workflowJobExecutionDto({
|
|
125
|
-
id: '
|
|
125
|
+
id: '77777777-7777-4777-8777-000000000001',
|
|
126
126
|
job_id: BUILD_JOB_ID,
|
|
127
127
|
sequence: 1,
|
|
128
128
|
status: 'succeeded',
|
|
@@ -152,7 +152,7 @@ describe('WorkflowRunView', () => {
|
|
|
152
152
|
],
|
|
153
153
|
}),
|
|
154
154
|
workflowJobExecutionDto({
|
|
155
|
-
id: '
|
|
155
|
+
id: '77777777-7777-4777-8777-000000000002',
|
|
156
156
|
job_id: BUILD_JOB_ID,
|
|
157
157
|
sequence: 2,
|
|
158
158
|
status: 'failed',
|
|
@@ -427,7 +427,7 @@ describe('WorkflowRunView', () => {
|
|
|
427
427
|
status: 'succeeded',
|
|
428
428
|
job_executions: [
|
|
429
429
|
workflowJobExecutionDto({
|
|
430
|
-
id: '
|
|
430
|
+
id: '77777777-7777-4777-8777-000000000003',
|
|
431
431
|
job_id: DEPLOY_JOB_ID,
|
|
432
432
|
sequence: 2,
|
|
433
433
|
name: 'retry',
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type {WorkflowRunRerunModeDto} from '@shipfox/api-workflows-dto';
|
|
2
1
|
import {ApiError} from '@shipfox/client-api';
|
|
3
2
|
import {QueryLoadError} from '@shipfox/client-ui';
|
|
4
3
|
import {RelativeTimeProvider} from '@shipfox/react-ui/relative-time';
|
|
@@ -9,6 +8,7 @@ import {
|
|
|
9
8
|
type JobExecution,
|
|
10
9
|
resolveJobExecution,
|
|
11
10
|
type StepSourceLocation,
|
|
11
|
+
type WorkflowRunRerunMode,
|
|
12
12
|
} from '#core/workflow-run.js';
|
|
13
13
|
import {
|
|
14
14
|
type WorkflowRunSelectionInput,
|
|
@@ -160,7 +160,7 @@ function RunViewContent({
|
|
|
160
160
|
const highlightedLineRange =
|
|
161
161
|
sourceFocus?.location ?? resolvedSelection?.step?.sourceLocation ?? null;
|
|
162
162
|
const sourceSnapshot = runData.sourceSnapshot;
|
|
163
|
-
async function rerun(mode:
|
|
163
|
+
async function rerun(mode: WorkflowRunRerunMode) {
|
|
164
164
|
try {
|
|
165
165
|
const run = await rerunMutation.mutateAsync({workflowRunId: runData.id, mode});
|
|
166
166
|
toast.success('Re-run started');
|
|
@@ -61,6 +61,10 @@ async function captureHighlightedSourcePanel(
|
|
|
61
61
|
},
|
|
62
62
|
{timeout: 10_000},
|
|
63
63
|
);
|
|
64
|
+
const loadedCodeFonts = await document.fonts.load('400 14px "Commit Mono"');
|
|
65
|
+
if (!loadedCodeFonts.some((font) => font.status === 'loaded')) {
|
|
66
|
+
throw new Error('Commit Mono has not loaded yet');
|
|
67
|
+
}
|
|
64
68
|
await argosScreenshot(ctx, screenshotName);
|
|
65
69
|
}
|
|
66
70
|
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
WorkflowExecutionEventDto,
|
|
3
|
-
WorkflowRunJobExecutionDetailDto,
|
|
4
|
-
} from '@shipfox/api-workflows-dto';
|
|
5
1
|
import {type Duration, intervalToDuration} from 'date-fns';
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
export type JobExecutionStatus =
|
|
2
|
+
import type {Step} from './step.js';
|
|
3
|
+
|
|
4
|
+
export type JobExecutionStatus = 'pending' | 'running' | 'succeeded' | 'failed' | 'cancelled';
|
|
5
|
+
export interface WorkflowExecutionEvent {
|
|
6
|
+
source: string;
|
|
7
|
+
event: string;
|
|
8
|
+
deliveryId: string;
|
|
9
|
+
receivedAt: string;
|
|
10
|
+
data: unknown;
|
|
11
|
+
}
|
|
9
12
|
export type JobExecutionTime =
|
|
10
13
|
| {state: 'fixed'; elapsed: Duration}
|
|
11
14
|
| {state: 'live'; fromIso: string};
|
|
@@ -20,7 +23,7 @@ interface JobExecutionFields {
|
|
|
20
23
|
name: string;
|
|
21
24
|
status: JobExecutionStatus;
|
|
22
25
|
statusReason: string | null;
|
|
23
|
-
triggerEvents:
|
|
26
|
+
triggerEvents: WorkflowExecutionEvent[];
|
|
24
27
|
queuedAt: string | null;
|
|
25
28
|
startedAt: string | null;
|
|
26
29
|
finishedAt: string | null;
|
|
@@ -37,7 +40,7 @@ export class JobExecution {
|
|
|
37
40
|
name!: string;
|
|
38
41
|
status!: JobExecutionStatus;
|
|
39
42
|
statusReason!: string | null;
|
|
40
|
-
triggerEvents!:
|
|
43
|
+
triggerEvents!: WorkflowExecutionEvent[];
|
|
41
44
|
queuedAt!: string | null;
|
|
42
45
|
startedAt!: string | null;
|
|
43
46
|
finishedAt!: string | null;
|
|
@@ -63,25 +66,6 @@ export class JobExecution {
|
|
|
63
66
|
}
|
|
64
67
|
}
|
|
65
68
|
|
|
66
|
-
export function toJobExecution(dto: WorkflowRunJobExecutionDetailDto): JobExecution {
|
|
67
|
-
return new JobExecution({
|
|
68
|
-
id: dto.id,
|
|
69
|
-
jobId: dto.job_id,
|
|
70
|
-
sequence: dto.sequence,
|
|
71
|
-
name: dto.name,
|
|
72
|
-
status: dto.status,
|
|
73
|
-
statusReason: dto.status_reason,
|
|
74
|
-
triggerEvents: dto.trigger_events ?? [],
|
|
75
|
-
queuedAt: dto.queued_at ?? null,
|
|
76
|
-
startedAt: dto.started_at ?? null,
|
|
77
|
-
finishedAt: dto.finished_at ?? null,
|
|
78
|
-
timedOutAt: dto.timed_out_at ?? null,
|
|
79
|
-
createdAt: dto.created_at,
|
|
80
|
-
updatedAt: dto.updated_at,
|
|
81
|
-
steps: dto.steps.map(toStep),
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
|
|
85
69
|
export function jobExecutionQueueTimeFromTimestamps({
|
|
86
70
|
queuedAt,
|
|
87
71
|
startedAt,
|
package/src/core/entities/job.ts
CHANGED
|
@@ -1,23 +1,45 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
export
|
|
20
|
-
|
|
1
|
+
import type {JobExecution, JobExecutionDisplayDuration} from './job-execution.js';
|
|
2
|
+
|
|
3
|
+
export type JobStatus = 'pending' | 'running' | 'succeeded' | 'failed' | 'cancelled' | 'skipped';
|
|
4
|
+
export type JobMode = 'one_shot' | 'listening';
|
|
5
|
+
export type ListenerStatus = 'inactive' | 'listening' | 'resolved';
|
|
6
|
+
export type ResolutionReason = 'until' | 'timeout' | 'max_executions' | 'cancelled';
|
|
7
|
+
export type JobStatusReason =
|
|
8
|
+
| 'dependency_not_completed'
|
|
9
|
+
| 'condition_false'
|
|
10
|
+
| 'default_gate_rejected'
|
|
11
|
+
| 'condition_rejected'
|
|
12
|
+
| 'condition_errored'
|
|
13
|
+
| 'user_cancelled'
|
|
14
|
+
| 'run_cancelled'
|
|
15
|
+
| 'timed_out'
|
|
16
|
+
| 'runner_lost'
|
|
17
|
+
| 'step_failed'
|
|
18
|
+
| 'unknown';
|
|
19
|
+
export interface JobListening {
|
|
20
|
+
on: Array<{
|
|
21
|
+
source: string;
|
|
22
|
+
event: string;
|
|
23
|
+
inputs?: Record<string, unknown> | undefined;
|
|
24
|
+
filter?: string | undefined;
|
|
25
|
+
}>;
|
|
26
|
+
until: Array<{
|
|
27
|
+
source: string;
|
|
28
|
+
event: string;
|
|
29
|
+
inputs?: Record<string, unknown> | undefined;
|
|
30
|
+
filter?: string | undefined;
|
|
31
|
+
}> | null;
|
|
32
|
+
timeoutMs: number | null;
|
|
33
|
+
maxExecutions: number | null;
|
|
34
|
+
batch: {
|
|
35
|
+
debounceMs?: number | undefined;
|
|
36
|
+
maxSize?: number | undefined;
|
|
37
|
+
maxWaitMs?: number | undefined;
|
|
38
|
+
} | null;
|
|
39
|
+
onResolve: 'finish' | 'cancel';
|
|
40
|
+
executionTimeoutMs: number | null;
|
|
41
|
+
name: string | null;
|
|
42
|
+
}
|
|
21
43
|
|
|
22
44
|
export type JobDisplayDuration = JobExecutionDisplayDuration;
|
|
23
45
|
|
|
@@ -48,7 +70,7 @@ interface JobFields {
|
|
|
48
70
|
status: JobStatus;
|
|
49
71
|
statusReason: JobStatusReason | null;
|
|
50
72
|
carriedOver: boolean;
|
|
51
|
-
listening:
|
|
73
|
+
listening: JobListening | null;
|
|
52
74
|
listenerStatus: ListenerStatus;
|
|
53
75
|
resolutionReason: ResolutionReason | null;
|
|
54
76
|
dependencies: string[];
|
|
@@ -67,7 +89,7 @@ export class Job {
|
|
|
67
89
|
status!: JobStatus;
|
|
68
90
|
statusReason!: JobStatusReason | null;
|
|
69
91
|
carriedOver!: boolean;
|
|
70
|
-
listening!:
|
|
92
|
+
listening!: JobListening | null;
|
|
71
93
|
listenerStatus!: ListenerStatus;
|
|
72
94
|
resolutionReason!: ResolutionReason | null;
|
|
73
95
|
dependencies!: string[];
|
|
@@ -124,26 +146,3 @@ export function defaultJobExecution(job: Job): JobExecution | undefined {
|
|
|
124
146
|
return jobExecution.sequence > latest.sequence ? jobExecution : latest;
|
|
125
147
|
}, undefined);
|
|
126
148
|
}
|
|
127
|
-
|
|
128
|
-
export function toJob(dto: WorkflowRunJobDetailDto): Job {
|
|
129
|
-
const jobExecutions = dto.job_executions.map(toJobExecution);
|
|
130
|
-
|
|
131
|
-
return new Job({
|
|
132
|
-
id: dto.id,
|
|
133
|
-
runAttemptId: dto.run_attempt_id,
|
|
134
|
-
key: dto.key,
|
|
135
|
-
name: dto.name,
|
|
136
|
-
mode: dto.mode,
|
|
137
|
-
status: dto.status,
|
|
138
|
-
statusReason: dto.status_reason,
|
|
139
|
-
carriedOver: dto.carried_over,
|
|
140
|
-
listening: dto.listening,
|
|
141
|
-
listenerStatus: dto.listener_status,
|
|
142
|
-
resolutionReason: dto.resolution_reason,
|
|
143
|
-
dependencies: dto.dependencies,
|
|
144
|
-
position: dto.position,
|
|
145
|
-
createdAt: dto.created_at,
|
|
146
|
-
updatedAt: dto.updated_at,
|
|
147
|
-
jobExecutions,
|
|
148
|
-
});
|
|
149
|
-
}
|
|
@@ -1,7 +1,14 @@
|
|
|
1
|
-
import type {StepAttemptDto, StepGateResultDto} from '@shipfox/api-workflows-dto';
|
|
2
1
|
import {type Duration, intervalToDuration} from 'date-fns';
|
|
3
2
|
|
|
4
|
-
export type StepGateResult =
|
|
3
|
+
export type StepGateResult =
|
|
4
|
+
| {kind: 'none'}
|
|
5
|
+
| {kind: 'not_evaluated'}
|
|
6
|
+
| {kind: 'passed'; passed: true; source: string; exitCode: number | null}
|
|
7
|
+
| {kind: 'failed'; passed: false; source: string; exitCode: number | null}
|
|
8
|
+
| {kind: 'uncheckable'; passed: false; uncheckable: true; reason: string; exitCode: number | null}
|
|
9
|
+
| {kind: 'evaluation_error'; reason: string; exitCode: number | null}
|
|
10
|
+
| {kind: 'unknown'; data: Record<string, unknown>}
|
|
11
|
+
| null;
|
|
5
12
|
export type StepAttemptDisplayDuration =
|
|
6
13
|
| {state: 'fixed'; elapsed: Duration}
|
|
7
14
|
| {state: 'live'; fromIso: string};
|
|
@@ -46,24 +53,6 @@ export class StepAttempt {
|
|
|
46
53
|
}
|
|
47
54
|
}
|
|
48
55
|
|
|
49
|
-
export function toStepAttempt(dto: StepAttemptDto, jobExecutionId: string): StepAttempt {
|
|
50
|
-
return new StepAttempt({
|
|
51
|
-
id: dto.id,
|
|
52
|
-
stepId: dto.step_id,
|
|
53
|
-
jobExecutionId,
|
|
54
|
-
attempt: dto.attempt,
|
|
55
|
-
executionOrder: dto.execution_order,
|
|
56
|
-
status: dto.status,
|
|
57
|
-
exitCode: dto.exit_code ?? null,
|
|
58
|
-
output: dto.output ?? null,
|
|
59
|
-
error: dto.error ?? null,
|
|
60
|
-
gateResult: dto.gate_result ?? null,
|
|
61
|
-
restartFeedback: dto.restart_feedback ?? null,
|
|
62
|
-
startedAt: dto.started_at,
|
|
63
|
-
finishedAt: dto.finished_at ?? null,
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
|
|
67
56
|
export function stepAttemptDisplayDurationFromTimestamps({
|
|
68
57
|
startedAt,
|
|
69
58
|
finishedAt,
|
|
@@ -1,15 +1,42 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
AgentConfigIssueDto,
|
|
3
|
-
StepErrorCategoryDto,
|
|
4
|
-
StepErrorReasonDto,
|
|
5
|
-
StepSourceLocationDto,
|
|
6
|
-
WorkflowRunStepDetailDto,
|
|
7
|
-
} from '@shipfox/api-workflows-dto';
|
|
8
|
-
import {type StepAttempt, toStepAttempt} from './step-attempt.js';
|
|
1
|
+
import type {StepAttempt} from './step-attempt.js';
|
|
9
2
|
|
|
10
|
-
export type StepErrorReason =
|
|
11
|
-
|
|
12
|
-
|
|
3
|
+
export type StepErrorReason =
|
|
4
|
+
| 'checkout_failed'
|
|
5
|
+
| 'checkout_auth_failed'
|
|
6
|
+
| 'checkout_unavailable'
|
|
7
|
+
| 'git_unavailable'
|
|
8
|
+
| 'workspace_prep_failed'
|
|
9
|
+
| 'setup_aborted'
|
|
10
|
+
| 'config_unresolvable'
|
|
11
|
+
| 'output_invalid'
|
|
12
|
+
| 'agent_config_invalid'
|
|
13
|
+
| 'agent_invocation_failed';
|
|
14
|
+
export type AgentConfigIssue =
|
|
15
|
+
| 'step_config_invalid'
|
|
16
|
+
| 'provider_not_configured'
|
|
17
|
+
| 'provider_unsupported'
|
|
18
|
+
| 'model_unavailable'
|
|
19
|
+
| 'credentials_invalid';
|
|
20
|
+
export type StepErrorCategory = 'setup' | 'user';
|
|
21
|
+
export const STEP_ERROR_REASONS = new Set<StepErrorReason>([
|
|
22
|
+
'checkout_failed',
|
|
23
|
+
'checkout_auth_failed',
|
|
24
|
+
'checkout_unavailable',
|
|
25
|
+
'git_unavailable',
|
|
26
|
+
'workspace_prep_failed',
|
|
27
|
+
'setup_aborted',
|
|
28
|
+
'config_unresolvable',
|
|
29
|
+
'output_invalid',
|
|
30
|
+
'agent_config_invalid',
|
|
31
|
+
'agent_invocation_failed',
|
|
32
|
+
]);
|
|
33
|
+
export const AGENT_CONFIG_ISSUES = new Set<AgentConfigIssue>([
|
|
34
|
+
'step_config_invalid',
|
|
35
|
+
'provider_not_configured',
|
|
36
|
+
'provider_unsupported',
|
|
37
|
+
'model_unavailable',
|
|
38
|
+
'credentials_invalid',
|
|
39
|
+
]);
|
|
13
40
|
|
|
14
41
|
export interface StepSourceLocation {
|
|
15
42
|
startLine: number;
|
|
@@ -48,58 +75,3 @@ export interface Step {
|
|
|
48
75
|
updatedAt: string;
|
|
49
76
|
attempts: StepAttempt[];
|
|
50
77
|
}
|
|
51
|
-
|
|
52
|
-
export function toStep(dto: WorkflowRunStepDetailDto): Step {
|
|
53
|
-
return {
|
|
54
|
-
id: dto.id,
|
|
55
|
-
jobExecutionId: dto.job_execution_id,
|
|
56
|
-
key: dto.key,
|
|
57
|
-
name: dto.name,
|
|
58
|
-
sourceLocation: dto.source_location ? toStepSourceLocation(dto.source_location) : null,
|
|
59
|
-
status: dto.status,
|
|
60
|
-
type: dto.type,
|
|
61
|
-
config: dto.config,
|
|
62
|
-
agentConfig: toAgentStepConfig(dto),
|
|
63
|
-
error: dto.error ? toStepError(dto.error) : null,
|
|
64
|
-
position: dto.position,
|
|
65
|
-
currentAttempt: dto.current_attempt,
|
|
66
|
-
createdAt: dto.created_at,
|
|
67
|
-
updatedAt: dto.updated_at,
|
|
68
|
-
attempts: dto.attempts.map((attempt) => toStepAttempt(attempt, dto.job_execution_id)),
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function toStepSourceLocation(dto: StepSourceLocationDto): StepSourceLocation {
|
|
73
|
-
return {
|
|
74
|
-
startLine: dto.start_line,
|
|
75
|
-
endLine: dto.end_line,
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function toStepError(dto: NonNullable<WorkflowRunStepDetailDto['error']>): StepError {
|
|
80
|
-
return {
|
|
81
|
-
message: dto.message,
|
|
82
|
-
exitCode: dto.exit_code ?? null,
|
|
83
|
-
signal: dto.signal,
|
|
84
|
-
reason: dto.reason,
|
|
85
|
-
agentConfigIssue: dto.agent_config_issue,
|
|
86
|
-
category: dto.category,
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function toAgentStepConfig(dto: WorkflowRunStepDetailDto): AgentStepConfig | null {
|
|
91
|
-
if (dto.type !== 'agent') return null;
|
|
92
|
-
|
|
93
|
-
return {
|
|
94
|
-
provider: stringConfigValue(dto.config.provider),
|
|
95
|
-
model: stringConfigValue(dto.config.model),
|
|
96
|
-
thinking: stringConfigValue(dto.config.thinking),
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function stringConfigValue(value: unknown): string | null {
|
|
101
|
-
if (typeof value !== 'string') return null;
|
|
102
|
-
|
|
103
|
-
const trimmedValue = value.trim();
|
|
104
|
-
return trimmedValue ? trimmedValue : null;
|
|
105
|
-
}
|