@shipfox/api-definitions 13.1.0 → 14.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 +11 -11
- package/CHANGELOG.md +35 -0
- package/dist/core/entities/index.d.ts +1 -1
- package/dist/core/entities/index.d.ts.map +1 -1
- package/dist/core/entities/index.js +1 -1
- package/dist/core/entities/index.js.map +1 -1
- package/dist/core/entities/integration-context.d.ts +4 -0
- package/dist/core/entities/integration-context.d.ts.map +1 -1
- package/dist/core/entities/integration-context.js.map +1 -1
- package/dist/core/entities/sync-state.d.ts +10 -4
- package/dist/core/entities/sync-state.d.ts.map +1 -1
- package/dist/core/entities/sync-state.js +26 -7
- package/dist/core/entities/sync-state.js.map +1 -1
- package/dist/core/entities/validation-diagnostic.d.ts +8 -0
- package/dist/core/entities/validation-diagnostic.d.ts.map +1 -0
- package/dist/core/entities/validation-diagnostic.js +3 -0
- package/dist/core/entities/validation-diagnostic.js.map +1 -0
- package/dist/core/entities/workflow-definition.d.ts +2 -0
- package/dist/core/entities/workflow-definition.d.ts.map +1 -1
- package/dist/core/entities/workflow-definition.js.map +1 -1
- package/dist/core/errors.d.ts +10 -0
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +9 -0
- package/dist/core/errors.js.map +1 -1
- package/dist/core/index.d.ts +2 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +2 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/integrations.d.ts +3 -1
- package/dist/core/integrations.d.ts.map +1 -1
- package/dist/core/integrations.js +10 -2
- package/dist/core/integrations.js.map +1 -1
- package/dist/core/parse-definition.d.ts +4 -4
- package/dist/core/parse-definition.d.ts.map +1 -1
- package/dist/core/parse-definition.js +5 -5
- package/dist/core/parse-definition.js.map +1 -1
- package/dist/core/resolve-definition-at-ref.d.ts +66 -0
- package/dist/core/resolve-definition-at-ref.d.ts.map +1 -0
- package/dist/core/resolve-definition-at-ref.js +374 -0
- package/dist/core/resolve-definition-at-ref.js.map +1 -0
- package/dist/core/sync-definitions.d.ts +2 -2
- package/dist/core/sync-definitions.d.ts.map +1 -1
- package/dist/core/sync-definitions.js +5 -5
- package/dist/core/sync-definitions.js.map +1 -1
- package/dist/core/validate-definition.d.ts +3 -3
- package/dist/core/validate-definition.d.ts.map +1 -1
- package/dist/core/validate-definition.js +17 -12
- package/dist/core/validate-definition.js.map +1 -1
- package/dist/core/workflow-model/cron-trigger.d.ts +1 -1
- package/dist/core/workflow-model/cron-trigger.d.ts.map +1 -1
- package/dist/core/workflow-model/cron-trigger.js +9 -5
- package/dist/core/workflow-model/cron-trigger.js.map +1 -1
- package/dist/core/workflow-model/index.d.ts +1 -1
- package/dist/core/workflow-model/index.d.ts.map +1 -1
- package/dist/core/workflow-model/index.js.map +1 -1
- package/dist/core/workflow-model/invalid-workflow-model-error.d.ts +4 -2
- package/dist/core/workflow-model/invalid-workflow-model-error.d.ts.map +1 -1
- package/dist/core/workflow-model/invalid-workflow-model-error.js.map +1 -1
- package/dist/core/workflow-model/normalize-job-listening.d.ts.map +1 -1
- package/dist/core/workflow-model/normalize-job-listening.js +51 -30
- package/dist/core/workflow-model/normalize-job-listening.js.map +1 -1
- package/dist/core/workflow-model/normalize-triggers.d.ts +1 -1
- package/dist/core/workflow-model/normalize-triggers.d.ts.map +1 -1
- package/dist/core/workflow-model/normalize-triggers.js +48 -18
- package/dist/core/workflow-model/normalize-triggers.js.map +1 -1
- package/dist/core/workflow-model/normalize-workflow-document.d.ts +1 -1
- package/dist/core/workflow-model/normalize-workflow-document.d.ts.map +1 -1
- package/dist/core/workflow-model/normalize-workflow-document.js +3 -3
- package/dist/core/workflow-model/normalize-workflow-document.js.map +1 -1
- package/dist/core/workflow-model/parse-interpolation-field.d.ts.map +1 -1
- package/dist/core/workflow-model/parse-interpolation-field.js +1 -15
- package/dist/core/workflow-model/parse-interpolation-field.js.map +1 -1
- package/dist/core/workflow-model/validate-predicate-expression.d.ts +3 -1
- package/dist/core/workflow-model/validate-predicate-expression.d.ts.map +1 -1
- package/dist/core/workflow-model/validate-predicate-expression.js +3 -0
- package/dist/core/workflow-model/validate-predicate-expression.js.map +1 -1
- package/dist/core/workflow-model/validation-issue.d.ts +2 -1
- package/dist/core/workflow-model/validation-issue.d.ts.map +1 -1
- package/dist/core/workflow-model/validation-issue.js +12 -15
- package/dist/core/workflow-model/validation-issue.js.map +1 -1
- package/dist/db/db.d.ts +196 -4
- package/dist/db/db.d.ts.map +1 -1
- package/dist/db/db.js +3 -1
- package/dist/db/db.js.map +1 -1
- package/dist/db/definition-triggers.js +3 -1
- package/dist/db/definition-triggers.js.map +1 -1
- package/dist/db/definitions.d.ts +17 -0
- package/dist/db/definitions.d.ts.map +1 -1
- package/dist/db/definitions.js +190 -23
- package/dist/db/definitions.js.map +1 -1
- package/dist/db/index.d.ts +2 -1
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +2 -1
- package/dist/db/index.js.map +1 -1
- package/dist/db/schema/definitions.d.ts +17 -0
- package/dist/db/schema/definitions.d.ts.map +1 -1
- package/dist/db/schema/definitions.js +8 -0
- package/dist/db/schema/definitions.js.map +1 -1
- package/dist/db/schema/sync-states.d.ts +7 -3
- package/dist/db/schema/sync-states.d.ts.map +1 -1
- package/dist/db/schema/sync-states.js +5 -1
- package/dist/db/schema/sync-states.js.map +1 -1
- package/dist/db/schema/workflows.d.ts +78 -0
- package/dist/db/schema/workflows.d.ts.map +1 -0
- package/dist/db/schema/workflows.js +17 -0
- package/dist/db/schema/workflows.js.map +1 -0
- package/dist/db/sync-states.d.ts +2 -2
- package/dist/db/sync-states.d.ts.map +1 -1
- package/dist/db/sync-states.js +4 -4
- package/dist/db/sync-states.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/metrics/index.d.ts +2 -0
- package/dist/metrics/index.d.ts.map +1 -0
- package/dist/metrics/index.js +3 -0
- package/dist/metrics/index.js.map +1 -0
- package/dist/metrics/instance.d.ts +4 -0
- package/dist/metrics/instance.d.ts.map +1 -0
- package/dist/metrics/instance.js +19 -0
- package/dist/metrics/instance.js.map +1 -0
- package/dist/presentation/dto/definition.d.ts.map +1 -1
- package/dist/presentation/dto/definition.js +11 -4
- package/dist/presentation/dto/definition.js.map +1 -1
- package/dist/presentation/inter-module.d.ts +9 -1
- package/dist/presentation/inter-module.d.ts.map +1 -1
- package/dist/presentation/inter-module.js +66 -2
- package/dist/presentation/inter-module.js.map +1 -1
- package/dist/presentation/routes/create-definition.d.ts.map +1 -1
- package/dist/presentation/routes/create-definition.js +16 -3
- package/dist/presentation/routes/create-definition.js.map +1 -1
- package/dist/presentation/routes/get-definition.d.ts.map +1 -1
- package/dist/presentation/routes/get-definition.js +18 -4
- package/dist/presentation/routes/get-definition.js.map +1 -1
- package/dist/presentation/routes/project-access.d.ts +1 -0
- package/dist/presentation/routes/project-access.d.ts.map +1 -1
- package/dist/presentation/routes/validate-definition.js +3 -3
- package/dist/presentation/routes/validate-definition.js.map +1 -1
- package/dist/temporal/activities/sync-activities.d.ts +3 -3
- package/dist/temporal/activities/sync-activities.d.ts.map +1 -1
- package/dist/temporal/activities/sync-activities.js +7 -4
- package/dist/temporal/activities/sync-activities.js.map +1 -1
- package/dist/temporal/workflows/definition-sync.js +1 -1
- package/dist/temporal/workflows/definition-sync.js.map +1 -1
- package/dist/temporal/workflows/index.bundle.js +50 -22
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/drizzle/0003_nebulous_nemesis.sql +11 -0
- package/drizzle/0004_workflow_ref_lookup.sql +1 -0
- package/drizzle/meta/0003_snapshot.json +627 -0
- package/drizzle/meta/0004_snapshot.json +649 -0
- package/drizzle/meta/_journal.json +14 -0
- package/drizzle.config.ts +1 -0
- package/package.json +7 -7
- package/src/core/entities/index.ts +1 -1
- package/src/core/entities/integration-context.ts +4 -0
- package/src/core/entities/sync-state.test.ts +49 -0
- package/src/core/entities/sync-state.ts +35 -12
- package/src/core/entities/validation-diagnostic.ts +8 -0
- package/src/core/entities/workflow-definition.ts +2 -0
- package/src/core/errors.ts +26 -0
- package/src/core/index.ts +12 -0
- package/src/core/integrations.test.ts +38 -0
- package/src/core/integrations.ts +10 -1
- package/src/core/parse-definition-diagnostics.test.ts +47 -0
- package/src/core/parse-definition.test.ts +29 -5
- package/src/core/parse-definition.ts +7 -7
- package/src/core/resolve-definition-at-ref.test.ts +792 -0
- package/src/core/resolve-definition-at-ref.ts +551 -0
- package/src/core/sync-definitions.test.ts +61 -2
- package/src/core/sync-definitions.ts +7 -7
- package/src/core/validate-definition.test.ts +31 -22
- package/src/core/validate-definition.ts +21 -13
- package/src/core/workflow-model/cron-trigger.ts +6 -2
- package/src/core/workflow-model/index.ts +1 -0
- package/src/core/workflow-model/invalid-workflow-model-error.ts +5 -1
- package/src/core/workflow-model/normalize-job-listening.ts +55 -23
- package/src/core/workflow-model/normalize-triggers.ts +44 -15
- package/src/core/workflow-model/normalize-workflow-document.test.ts +444 -36
- package/src/core/workflow-model/normalize-workflow-document.ts +8 -4
- package/src/core/workflow-model/parse-interpolation-field.ts +1 -23
- package/src/core/workflow-model/validate-predicate-expression.ts +6 -0
- package/src/core/workflow-model/validation-issue.ts +8 -13
- package/src/db/db.ts +7 -1
- package/src/db/definition-triggers.test.ts +44 -0
- package/src/db/definition-triggers.ts +1 -1
- package/src/db/definitions.test.ts +300 -1
- package/src/db/definitions.ts +282 -28
- package/src/db/index.ts +2 -0
- package/src/db/schema/definitions.test.ts +1 -0
- package/src/db/schema/definitions.ts +11 -0
- package/src/db/schema/sync-states.ts +12 -3
- package/src/db/schema/workflows.ts +23 -0
- package/src/db/sync-states.test.ts +87 -18
- package/src/db/sync-states.ts +6 -6
- package/src/db/workflow-lineage-migration.test.ts +158 -0
- package/src/index.ts +3 -1
- package/src/metrics/index.ts +1 -0
- package/src/metrics/instance.test.ts +50 -0
- package/src/metrics/instance.ts +23 -0
- package/src/presentation/dto/definition.test.ts +89 -1
- package/src/presentation/dto/definition.ts +8 -5
- package/src/presentation/inter-module.test.ts +177 -0
- package/src/presentation/inter-module.ts +77 -4
- package/src/presentation/routes/create-definition.test.ts +38 -0
- package/src/presentation/routes/create-definition.ts +20 -3
- package/src/presentation/routes/get-definition.test.ts +125 -11
- package/src/presentation/routes/get-definition.ts +21 -4
- package/src/presentation/routes/list-definitions.test.ts +1 -1
- package/src/presentation/routes/validate-definition.test.ts +1 -1
- package/src/presentation/routes/validate-definition.ts +3 -3
- package/src/presentation/subscribers/on-integration-connection-available.test.ts +3 -0
- package/src/temporal/activities/sync-activities.test.ts +46 -2
- package/src/temporal/activities/sync-activities.ts +9 -7
- package/src/temporal/workflows/definition-sync.ts +1 -1
- package/test/factories/definition.ts +1 -0
- package/test/globalSetup.ts +2 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/dist/core/entities/validation-warning.d.ts +0 -6
- package/dist/core/entities/validation-warning.d.ts.map +0 -1
- package/dist/core/entities/validation-warning.js +0 -3
- package/dist/core/entities/validation-warning.js.map +0 -1
- package/src/core/entities/validation-warning.ts +0 -5
- package/src/core/parse-definition-warnings.test.ts +0 -33
|
@@ -0,0 +1,551 @@
|
|
|
1
|
+
import type {AgentInterModuleClient} from '@shipfox/api-agent-dto/inter-module';
|
|
2
|
+
import type {
|
|
3
|
+
TriggerDto,
|
|
4
|
+
WorkflowModelSnapshot,
|
|
5
|
+
WorkflowSourceSnapshot,
|
|
6
|
+
} from '@shipfox/api-definitions-dto';
|
|
7
|
+
import {createWorkflowModelSnapshot} from '@shipfox/api-definitions-dto';
|
|
8
|
+
import {
|
|
9
|
+
type IntegrationsModuleClient,
|
|
10
|
+
integrationsInterModuleContract,
|
|
11
|
+
} from '@shipfox/api-integration-core-dto/inter-module';
|
|
12
|
+
import type {ProjectsModuleClient} from '@shipfox/api-projects-dto/inter-module';
|
|
13
|
+
import {isInterModuleKnownError} from '@shipfox/inter-module';
|
|
14
|
+
import {boundedMap} from '@shipfox/node-module';
|
|
15
|
+
import {definitionTriggersFor} from '#db/definition-triggers.js';
|
|
16
|
+
import {findOrCreateWorkflowLineage} from '#db/definitions.js';
|
|
17
|
+
import {recordDefinitionRefResolution} from '#metrics/index.js';
|
|
18
|
+
import type {ValidationDiagnostic} from './entities/validation-diagnostic.js';
|
|
19
|
+
import {
|
|
20
|
+
DefinitionAtRefError,
|
|
21
|
+
type DefinitionAtRefErrorCode,
|
|
22
|
+
DefinitionParseError,
|
|
23
|
+
} from './errors.js';
|
|
24
|
+
import {hasAgentStepIntegrations} from './has-agent-step-integrations.js';
|
|
25
|
+
import {loadIntegrationValidationContext} from './integrations.js';
|
|
26
|
+
import type {ParsedDefinition} from './parse-definition.js';
|
|
27
|
+
import {parseDefinitionWithDiagnostics} from './parse-definition.js';
|
|
28
|
+
import {
|
|
29
|
+
DEFAULT_WORKFLOW_PATH,
|
|
30
|
+
FILE_FETCH_CONCURRENCY,
|
|
31
|
+
MAX_WORKFLOW_FILE_BYTES,
|
|
32
|
+
MAX_WORKFLOW_FILES,
|
|
33
|
+
} from './sync-definitions.js';
|
|
34
|
+
import type {ValidationError} from './validate-definition.js';
|
|
35
|
+
|
|
36
|
+
export interface ResolveDefinitionAtRefParams {
|
|
37
|
+
projectId: string;
|
|
38
|
+
ref: string;
|
|
39
|
+
configPath: string;
|
|
40
|
+
expectedCommit?: string | undefined;
|
|
41
|
+
projects: ProjectsModuleClient;
|
|
42
|
+
agent: AgentInterModuleClient;
|
|
43
|
+
integrations: IntegrationsModuleClient;
|
|
44
|
+
signal?: AbortSignal;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface ValidationWarning {
|
|
48
|
+
code: string;
|
|
49
|
+
message: string;
|
|
50
|
+
path?: string | undefined;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface ResolvedDefinitionAtRef {
|
|
54
|
+
workflow: {id: string; configPath: string};
|
|
55
|
+
commit: string;
|
|
56
|
+
model: WorkflowModelSnapshot;
|
|
57
|
+
sourceSnapshot: WorkflowSourceSnapshot;
|
|
58
|
+
triggers: Record<string, TriggerDto>;
|
|
59
|
+
warnings: ValidationWarning[];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface ListDefinitionsAtRefParams {
|
|
63
|
+
projectId: string;
|
|
64
|
+
ref: string;
|
|
65
|
+
projects: ProjectsModuleClient;
|
|
66
|
+
agent: AgentInterModuleClient;
|
|
67
|
+
integrations: IntegrationsModuleClient;
|
|
68
|
+
signal?: AbortSignal;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface DefinitionAtRefFile {
|
|
72
|
+
configPath: string;
|
|
73
|
+
name: string | null;
|
|
74
|
+
valid: boolean;
|
|
75
|
+
errors: ValidationError[];
|
|
76
|
+
warnings: ValidationWarning[];
|
|
77
|
+
triggers: Record<string, TriggerDto>;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface DefinitionsAtRefListing {
|
|
81
|
+
commit: string;
|
|
82
|
+
files: DefinitionAtRefFile[];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
interface ResolvedProjectSource {
|
|
86
|
+
workspaceId: string;
|
|
87
|
+
connectionId: string;
|
|
88
|
+
externalRepositoryId: string;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Resolves a workflow definition file at a git ref without persisting it.
|
|
93
|
+
* The ref is pinned to a commit, the file is fetched at that commit, and the
|
|
94
|
+
* result is validated with the sync pipeline. Only the workflow lineage row is
|
|
95
|
+
* created so the dev run can be numbered; no definition row and no outbox
|
|
96
|
+
* event are written.
|
|
97
|
+
*/
|
|
98
|
+
export async function resolveDefinitionAtRef(
|
|
99
|
+
params: ResolveDefinitionAtRefParams,
|
|
100
|
+
): Promise<ResolvedDefinitionAtRef> {
|
|
101
|
+
try {
|
|
102
|
+
return await resolveDefinitionAtRefUnsafe(params);
|
|
103
|
+
} catch (error) {
|
|
104
|
+
if (error instanceof DefinitionAtRefError) {
|
|
105
|
+
recordDefinitionRefResolution(error.code);
|
|
106
|
+
}
|
|
107
|
+
throw error;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function resolveDefinitionAtRefUnsafe(
|
|
112
|
+
params: ResolveDefinitionAtRefParams,
|
|
113
|
+
): Promise<ResolvedDefinitionAtRef> {
|
|
114
|
+
throwIfAborted(params.signal);
|
|
115
|
+
const source = await requireProjectSource(params.projects, params.projectId, params.signal);
|
|
116
|
+
const commit = await resolveRefToCommit({
|
|
117
|
+
integrations: params.integrations,
|
|
118
|
+
source,
|
|
119
|
+
ref: params.ref,
|
|
120
|
+
signal: params.signal,
|
|
121
|
+
});
|
|
122
|
+
throwIfAborted(params.signal);
|
|
123
|
+
if (params.expectedCommit !== undefined && commit !== params.expectedCommit) {
|
|
124
|
+
throw new DefinitionAtRefError(
|
|
125
|
+
'ref-moved',
|
|
126
|
+
`Git ref ${params.ref} no longer resolves to the expected commit`,
|
|
127
|
+
{ref: params.ref, expectedCommit: params.expectedCommit},
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const snapshot = await fetchFileAtCommit({
|
|
132
|
+
integrations: params.integrations,
|
|
133
|
+
source,
|
|
134
|
+
commit,
|
|
135
|
+
ref: params.ref,
|
|
136
|
+
configPath: params.configPath,
|
|
137
|
+
signal: params.signal,
|
|
138
|
+
});
|
|
139
|
+
assertFileSize(snapshot.content, snapshot.path);
|
|
140
|
+
|
|
141
|
+
const parsed = await parseDefinitionAtRef({
|
|
142
|
+
content: snapshot.content,
|
|
143
|
+
agent: params.agent,
|
|
144
|
+
integrations: params.integrations,
|
|
145
|
+
source,
|
|
146
|
+
signal: params.signal,
|
|
147
|
+
});
|
|
148
|
+
throwIfAborted(params.signal);
|
|
149
|
+
const workflowId = await findOrCreateWorkflowLineage({
|
|
150
|
+
projectId: params.projectId,
|
|
151
|
+
configPath: params.configPath,
|
|
152
|
+
});
|
|
153
|
+
recordDefinitionRefResolution('resolved');
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
workflow: {id: workflowId, configPath: params.configPath},
|
|
157
|
+
commit,
|
|
158
|
+
model: createWorkflowModelSnapshot(parsed.model),
|
|
159
|
+
sourceSnapshot: {content: snapshot.content, format: 'yaml'},
|
|
160
|
+
triggers: definitionTriggersFor(parsed.model),
|
|
161
|
+
warnings: warningsFor(parsed.diagnostics),
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Lists the workflow files at a git ref with their validation state. Applies
|
|
167
|
+
* the sync limits (100 files, 1 MB per file). One invalid file does not fail
|
|
168
|
+
* the listing; it is reported as invalid.
|
|
169
|
+
*/
|
|
170
|
+
export async function listDefinitionsAtRef(
|
|
171
|
+
params: ListDefinitionsAtRefParams,
|
|
172
|
+
): Promise<DefinitionsAtRefListing> {
|
|
173
|
+
try {
|
|
174
|
+
return await listDefinitionsAtRefUnsafe(params);
|
|
175
|
+
} catch (error) {
|
|
176
|
+
if (error instanceof DefinitionAtRefError) {
|
|
177
|
+
recordDefinitionRefResolution(error.code);
|
|
178
|
+
}
|
|
179
|
+
throw error;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
type ListingEntry =
|
|
184
|
+
| {path: string; content: string; definition: ParsedDefinition}
|
|
185
|
+
| {path: string; errors: ValidationError[]};
|
|
186
|
+
|
|
187
|
+
async function listDefinitionsAtRefUnsafe(
|
|
188
|
+
params: ListDefinitionsAtRefParams,
|
|
189
|
+
): Promise<DefinitionsAtRefListing> {
|
|
190
|
+
throwIfAborted(params.signal);
|
|
191
|
+
const source = await requireProjectSource(params.projects, params.projectId, params.signal);
|
|
192
|
+
const commit = await resolveRefToCommit({
|
|
193
|
+
integrations: params.integrations,
|
|
194
|
+
source,
|
|
195
|
+
ref: params.ref,
|
|
196
|
+
signal: params.signal,
|
|
197
|
+
});
|
|
198
|
+
throwIfAborted(params.signal);
|
|
199
|
+
const paths = await listWorkflowFilesAtCommit({
|
|
200
|
+
integrations: params.integrations,
|
|
201
|
+
source,
|
|
202
|
+
commit,
|
|
203
|
+
signal: params.signal,
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
const fetched = await boundedMap(
|
|
207
|
+
paths,
|
|
208
|
+
FILE_FETCH_CONCURRENCY,
|
|
209
|
+
(path) =>
|
|
210
|
+
fetchListingFile({
|
|
211
|
+
integrations: params.integrations,
|
|
212
|
+
source,
|
|
213
|
+
commit,
|
|
214
|
+
ref: params.ref,
|
|
215
|
+
path,
|
|
216
|
+
signal: params.signal,
|
|
217
|
+
}),
|
|
218
|
+
{stopOnError: true, signal: params.signal},
|
|
219
|
+
);
|
|
220
|
+
throwIfAborted(params.signal);
|
|
221
|
+
const agentValidationCatalog = await callWithSignal(
|
|
222
|
+
params.agent.getValidationCatalog,
|
|
223
|
+
{},
|
|
224
|
+
params.signal,
|
|
225
|
+
);
|
|
226
|
+
throwIfAborted(params.signal);
|
|
227
|
+
let entries = fetched.map((entry) => {
|
|
228
|
+
throwIfAborted(params.signal);
|
|
229
|
+
return parseListingEntry(entry, {agentValidationCatalog});
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
const needsIntegrationContext = entries.some(
|
|
233
|
+
(entry) => 'definition' in entry && hasAgentStepIntegrations(entry.definition.document),
|
|
234
|
+
);
|
|
235
|
+
if (needsIntegrationContext) {
|
|
236
|
+
const integrationValidationContext = await loadAtRefIntegrationValidationContext({
|
|
237
|
+
integrations: params.integrations,
|
|
238
|
+
source,
|
|
239
|
+
signal: params.signal,
|
|
240
|
+
});
|
|
241
|
+
entries = entries.map((entry) =>
|
|
242
|
+
'definition' in entry && hasAgentStepIntegrations(entry.definition.document)
|
|
243
|
+
? parseListingEntry(
|
|
244
|
+
{path: entry.path, content: entry.content},
|
|
245
|
+
{agentValidationCatalog, integrationValidationContext},
|
|
246
|
+
)
|
|
247
|
+
: entry,
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
throwIfAborted(params.signal);
|
|
252
|
+
recordDefinitionRefResolution('resolved');
|
|
253
|
+
return {commit, files: entries.map((entry) => listingFileFor(entry))};
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
async function requireProjectSource(
|
|
257
|
+
projects: ProjectsModuleClient,
|
|
258
|
+
projectId: string,
|
|
259
|
+
signal: AbortSignal | undefined,
|
|
260
|
+
): Promise<ResolvedProjectSource> {
|
|
261
|
+
const {project} = await callWithSignal(projects.getProjectById, {projectId}, signal);
|
|
262
|
+
if (project === null) {
|
|
263
|
+
throw new DefinitionAtRefError('project-not-found', `Project not found: ${projectId}`, {
|
|
264
|
+
projectId,
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
return {
|
|
268
|
+
workspaceId: project.workspaceId,
|
|
269
|
+
connectionId: project.sourceConnectionId,
|
|
270
|
+
externalRepositoryId: project.sourceExternalRepositoryId,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
async function resolveRefToCommit(params: {
|
|
275
|
+
integrations: IntegrationsModuleClient;
|
|
276
|
+
source: ResolvedProjectSource;
|
|
277
|
+
ref: string;
|
|
278
|
+
signal: AbortSignal | undefined;
|
|
279
|
+
}): Promise<string> {
|
|
280
|
+
try {
|
|
281
|
+
const resolved = await callWithSignal(
|
|
282
|
+
params.integrations.resolveSourceRef,
|
|
283
|
+
{
|
|
284
|
+
workspaceId: params.source.workspaceId,
|
|
285
|
+
connectionId: params.source.connectionId,
|
|
286
|
+
externalRepositoryId: params.source.externalRepositoryId,
|
|
287
|
+
ref: params.ref,
|
|
288
|
+
},
|
|
289
|
+
params.signal,
|
|
290
|
+
);
|
|
291
|
+
return resolved.commit;
|
|
292
|
+
} catch (error) {
|
|
293
|
+
if (isInterModuleKnownError(integrationsInterModuleContract.methods.resolveSourceRef, error)) {
|
|
294
|
+
if (error.code === 'ref-not-found') {
|
|
295
|
+
throw new DefinitionAtRefError('ref-not-found', `Git ref not found: ${params.ref}`, {
|
|
296
|
+
ref: params.ref,
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
if (error.code === 'ref-invalid') {
|
|
300
|
+
throw new DefinitionAtRefError(
|
|
301
|
+
'ref-invalid',
|
|
302
|
+
`Git ref is not a resolvable branch or tag name: ${params.ref}`,
|
|
303
|
+
{ref: params.ref},
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
throw sourceUnavailable(error, 'The source repository is unavailable');
|
|
307
|
+
}
|
|
308
|
+
throw error;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
async function listWorkflowFilesAtCommit(params: {
|
|
313
|
+
integrations: IntegrationsModuleClient;
|
|
314
|
+
source: ResolvedProjectSource;
|
|
315
|
+
commit: string;
|
|
316
|
+
signal: AbortSignal | undefined;
|
|
317
|
+
}): Promise<string[]> {
|
|
318
|
+
let page: Awaited<ReturnType<IntegrationsModuleClient['listSourceFiles']>>;
|
|
319
|
+
try {
|
|
320
|
+
page = await callWithSignal(
|
|
321
|
+
params.integrations.listSourceFiles,
|
|
322
|
+
{
|
|
323
|
+
workspaceId: params.source.workspaceId,
|
|
324
|
+
connectionId: params.source.connectionId,
|
|
325
|
+
externalRepositoryId: params.source.externalRepositoryId,
|
|
326
|
+
ref: params.commit,
|
|
327
|
+
prefix: DEFAULT_WORKFLOW_PATH,
|
|
328
|
+
limit: MAX_WORKFLOW_FILES,
|
|
329
|
+
},
|
|
330
|
+
params.signal,
|
|
331
|
+
);
|
|
332
|
+
} catch (error) {
|
|
333
|
+
throwIfAborted(params.signal);
|
|
334
|
+
throw sourceUnavailable(error, 'The workflow files at the ref could not be listed');
|
|
335
|
+
}
|
|
336
|
+
if (page.nextCursor) {
|
|
337
|
+
throw new DefinitionAtRefError(
|
|
338
|
+
'too-many-files',
|
|
339
|
+
`More than ${MAX_WORKFLOW_FILES} workflow files were found`,
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
return page.files
|
|
343
|
+
.filter((file) => file.path.endsWith('.yml') || file.path.endsWith('.yaml'))
|
|
344
|
+
.map((file) => file.path);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
async function fetchFileAtCommit(params: {
|
|
348
|
+
integrations: IntegrationsModuleClient;
|
|
349
|
+
source: ResolvedProjectSource;
|
|
350
|
+
commit: string;
|
|
351
|
+
ref: string;
|
|
352
|
+
configPath: string;
|
|
353
|
+
signal: AbortSignal | undefined;
|
|
354
|
+
}): Promise<{path: string; content: string}> {
|
|
355
|
+
try {
|
|
356
|
+
return await callWithSignal(
|
|
357
|
+
params.integrations.fetchSourceFile,
|
|
358
|
+
{
|
|
359
|
+
workspaceId: params.source.workspaceId,
|
|
360
|
+
connectionId: params.source.connectionId,
|
|
361
|
+
externalRepositoryId: params.source.externalRepositoryId,
|
|
362
|
+
ref: params.commit,
|
|
363
|
+
path: params.configPath,
|
|
364
|
+
},
|
|
365
|
+
params.signal,
|
|
366
|
+
);
|
|
367
|
+
} catch (error) {
|
|
368
|
+
throwIfAborted(params.signal);
|
|
369
|
+
if (isInterModuleKnownError(integrationsInterModuleContract.methods.fetchSourceFile, error)) {
|
|
370
|
+
if (error.code === 'provider-failure' && error.details.reason === 'file-not-found') {
|
|
371
|
+
throw new DefinitionAtRefError(
|
|
372
|
+
'file-not-found',
|
|
373
|
+
`Workflow file not found at ${params.ref}: ${params.configPath}`,
|
|
374
|
+
{ref: params.ref, configPath: params.configPath},
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
throw sourceUnavailable(error, 'The workflow file at the ref could not be fetched');
|
|
378
|
+
}
|
|
379
|
+
throw error;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function assertFileSize(content: string, path: string): void {
|
|
384
|
+
if (Buffer.byteLength(content, 'utf8') > MAX_WORKFLOW_FILE_BYTES) {
|
|
385
|
+
throw new DefinitionAtRefError(
|
|
386
|
+
'content-too-large',
|
|
387
|
+
`Workflow file is larger than ${MAX_WORKFLOW_FILE_BYTES} bytes: ${path}`,
|
|
388
|
+
{configPath: path},
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
async function parseDefinitionAtRef(params: {
|
|
394
|
+
content: string;
|
|
395
|
+
agent: AgentInterModuleClient;
|
|
396
|
+
integrations: IntegrationsModuleClient;
|
|
397
|
+
source: ResolvedProjectSource;
|
|
398
|
+
signal: AbortSignal | undefined;
|
|
399
|
+
}): Promise<ParsedDefinition> {
|
|
400
|
+
const agentValidationCatalog = await callWithSignal(
|
|
401
|
+
params.agent.getValidationCatalog,
|
|
402
|
+
{},
|
|
403
|
+
params.signal,
|
|
404
|
+
);
|
|
405
|
+
throwIfAborted(params.signal);
|
|
406
|
+
const firstPass = parseWorkflowDefinition(params.content, {agentValidationCatalog});
|
|
407
|
+
if (!hasAgentStepIntegrations(firstPass.document)) return firstPass;
|
|
408
|
+
|
|
409
|
+
const integrationValidationContext = await loadAtRefIntegrationValidationContext({
|
|
410
|
+
integrations: params.integrations,
|
|
411
|
+
source: params.source,
|
|
412
|
+
signal: params.signal,
|
|
413
|
+
});
|
|
414
|
+
return parseWorkflowDefinition(params.content, {
|
|
415
|
+
agentValidationCatalog,
|
|
416
|
+
integrationValidationContext,
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
function parseWorkflowDefinition(
|
|
421
|
+
content: string,
|
|
422
|
+
options: Parameters<typeof parseDefinitionWithDiagnostics>[1],
|
|
423
|
+
): ParsedDefinition {
|
|
424
|
+
try {
|
|
425
|
+
return parseDefinitionWithDiagnostics(content, options);
|
|
426
|
+
} catch (error) {
|
|
427
|
+
if (error instanceof DefinitionParseError) {
|
|
428
|
+
throw new DefinitionAtRefError(
|
|
429
|
+
'invalid-definition',
|
|
430
|
+
`Invalid workflow definition: ${error.message}`,
|
|
431
|
+
{errors: (error.details ?? []) as ValidationError[]},
|
|
432
|
+
);
|
|
433
|
+
}
|
|
434
|
+
throw error;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
async function fetchListingFile(params: {
|
|
439
|
+
integrations: IntegrationsModuleClient;
|
|
440
|
+
source: ResolvedProjectSource;
|
|
441
|
+
commit: string;
|
|
442
|
+
ref: string;
|
|
443
|
+
path: string;
|
|
444
|
+
signal: AbortSignal | undefined;
|
|
445
|
+
}): Promise<{path: string; content: string} | {path: string; errors: ValidationError[]}> {
|
|
446
|
+
try {
|
|
447
|
+
const snapshot = await fetchFileAtCommit({
|
|
448
|
+
integrations: params.integrations,
|
|
449
|
+
source: params.source,
|
|
450
|
+
commit: params.commit,
|
|
451
|
+
ref: params.ref,
|
|
452
|
+
configPath: params.path,
|
|
453
|
+
signal: params.signal,
|
|
454
|
+
});
|
|
455
|
+
assertFileSize(snapshot.content, snapshot.path);
|
|
456
|
+
return {path: snapshot.path, content: snapshot.content};
|
|
457
|
+
} catch (error) {
|
|
458
|
+
if (error instanceof DefinitionAtRefError && isPerFileListingError(error.code)) {
|
|
459
|
+
return {path: params.path, errors: [{message: error.message}]};
|
|
460
|
+
}
|
|
461
|
+
throw error;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
function parseListingEntry(
|
|
466
|
+
entry: {path: string; content: string} | {path: string; errors: ValidationError[]},
|
|
467
|
+
options: Parameters<typeof parseDefinitionWithDiagnostics>[1],
|
|
468
|
+
): ListingEntry {
|
|
469
|
+
if ('errors' in entry) return entry;
|
|
470
|
+
try {
|
|
471
|
+
const definition = parseDefinitionWithDiagnostics(entry.content, options);
|
|
472
|
+
return {path: entry.path, content: entry.content, definition};
|
|
473
|
+
} catch (error) {
|
|
474
|
+
if (error instanceof DefinitionParseError) {
|
|
475
|
+
return {path: entry.path, errors: (error.details ?? []) as ValidationError[]};
|
|
476
|
+
}
|
|
477
|
+
throw error;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
function listingFileFor(entry: ListingEntry): DefinitionAtRefFile {
|
|
482
|
+
if ('definition' in entry) {
|
|
483
|
+
return {
|
|
484
|
+
configPath: entry.path,
|
|
485
|
+
name: entry.definition.document.name,
|
|
486
|
+
valid: true,
|
|
487
|
+
errors: [],
|
|
488
|
+
warnings: warningsFor(entry.definition.diagnostics),
|
|
489
|
+
triggers: definitionTriggersFor(entry.definition.model),
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
return {
|
|
493
|
+
configPath: entry.path,
|
|
494
|
+
name: null,
|
|
495
|
+
valid: false,
|
|
496
|
+
errors: entry.errors,
|
|
497
|
+
warnings: [],
|
|
498
|
+
triggers: {},
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
function warningsFor(diagnostics: readonly ValidationDiagnostic[]): ValidationWarning[] {
|
|
503
|
+
return diagnostics
|
|
504
|
+
.filter((diagnostic) => diagnostic.severity === 'warning')
|
|
505
|
+
.map((diagnostic) => ({
|
|
506
|
+
code: diagnostic.code,
|
|
507
|
+
message: diagnostic.message,
|
|
508
|
+
...(diagnostic.path === undefined ? {} : {path: diagnostic.path}),
|
|
509
|
+
}));
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
async function loadAtRefIntegrationValidationContext(params: {
|
|
513
|
+
integrations: IntegrationsModuleClient;
|
|
514
|
+
source: ResolvedProjectSource;
|
|
515
|
+
signal: AbortSignal | undefined;
|
|
516
|
+
}) {
|
|
517
|
+
try {
|
|
518
|
+
return await loadIntegrationValidationContext(
|
|
519
|
+
params.integrations,
|
|
520
|
+
params.source.workspaceId,
|
|
521
|
+
params.source.connectionId,
|
|
522
|
+
params.signal,
|
|
523
|
+
);
|
|
524
|
+
} catch (error) {
|
|
525
|
+
throwIfAborted(params.signal);
|
|
526
|
+
throw sourceUnavailable(error, 'Integration validation context is unavailable');
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
function isPerFileListingError(code: DefinitionAtRefErrorCode): boolean {
|
|
531
|
+
return code === 'file-not-found' || code === 'content-too-large' || code === 'invalid-definition';
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
function callWithSignal<Input, Output>(
|
|
535
|
+
method: (input: Input, options?: {signal?: AbortSignal}) => Promise<Output>,
|
|
536
|
+
input: Input,
|
|
537
|
+
signal: AbortSignal | undefined,
|
|
538
|
+
): Promise<Output> {
|
|
539
|
+
return signal === undefined ? method(input) : method(input, {signal});
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
function throwIfAborted(signal: AbortSignal | undefined): void {
|
|
543
|
+
if (signal?.aborted) throw signal.reason ?? new Error('Operation aborted');
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
function sourceUnavailable(error: unknown, message: string): DefinitionAtRefError {
|
|
547
|
+
return new DefinitionAtRefError(
|
|
548
|
+
'source-unavailable',
|
|
549
|
+
`${message}: ${error instanceof Error ? error.message : String(error)}`,
|
|
550
|
+
);
|
|
551
|
+
}
|
|
@@ -79,6 +79,11 @@ const integrationValidationContext = {
|
|
|
79
79
|
workspaceConnectionSnapshot: new Map([
|
|
80
80
|
['github-main', {id: 'connection-1', provider: 'github', capabilities: ['agent_tools']}],
|
|
81
81
|
]),
|
|
82
|
+
eventCatalogs: new Map([
|
|
83
|
+
['github', new Set(['push', 'pull_request.opened'])],
|
|
84
|
+
['webhook', new Set(['received'])],
|
|
85
|
+
]),
|
|
86
|
+
fixedEventProviders: new Set(['webhook']),
|
|
82
87
|
} satisfies IntegrationValidationContext;
|
|
83
88
|
|
|
84
89
|
function sourceControl(
|
|
@@ -243,7 +248,7 @@ describe('fetchAndParseWorkflows', () => {
|
|
|
243
248
|
expect(result[0]?.name).toBe('CI');
|
|
244
249
|
expect(result[0]?.path).toBe('.shipfox/workflows/ci.yml');
|
|
245
250
|
expect(result[0]?.contentHash).toMatch(LOWERCASE_SHA256_HEX_RE);
|
|
246
|
-
expect(result[0]?.
|
|
251
|
+
expect(result[0]?.diagnostics).toEqual([]);
|
|
247
252
|
});
|
|
248
253
|
|
|
249
254
|
it('keeps warning-only definitions available to the sync path', async () => {
|
|
@@ -262,15 +267,69 @@ describe('fetchAndParseWorkflows', () => {
|
|
|
262
267
|
}),
|
|
263
268
|
});
|
|
264
269
|
|
|
265
|
-
expect(result[0]?.
|
|
270
|
+
expect(result[0]?.diagnostics).toMatchObject([
|
|
266
271
|
{
|
|
267
272
|
code: 're-evaluating-command',
|
|
268
273
|
path: 'jobs.build.steps.0.run',
|
|
274
|
+
severity: 'warning',
|
|
269
275
|
},
|
|
270
276
|
]);
|
|
271
277
|
expect(result[0]?.definition.model.jobs[0]?.steps).toHaveLength(1);
|
|
272
278
|
});
|
|
273
279
|
|
|
280
|
+
it('keeps definitions with inert trigger-scoped errors available to the sync path', async () => {
|
|
281
|
+
const brokenTriggerPath = '.shipfox/workflows/broken-trigger.yml';
|
|
282
|
+
const validTriggerPath = '.shipfox/workflows/valid-trigger.yml';
|
|
283
|
+
const brokenTriggerYaml = `
|
|
284
|
+
name: Broken trigger
|
|
285
|
+
runner: ubuntu-latest
|
|
286
|
+
triggers:
|
|
287
|
+
nightly:
|
|
288
|
+
source: cron
|
|
289
|
+
event: tick
|
|
290
|
+
config:
|
|
291
|
+
schedule: "not a cron"
|
|
292
|
+
on_demand:
|
|
293
|
+
source: manual
|
|
294
|
+
event: fire
|
|
295
|
+
jobs:
|
|
296
|
+
build:
|
|
297
|
+
steps:
|
|
298
|
+
- run: pnpm test
|
|
299
|
+
`;
|
|
300
|
+
|
|
301
|
+
const fetchFile = vi.fn(({path}: {path: string}) =>
|
|
302
|
+
Promise.resolve({
|
|
303
|
+
path,
|
|
304
|
+
ref: 'main',
|
|
305
|
+
content: path === brokenTriggerPath ? brokenTriggerYaml : validYaml,
|
|
306
|
+
}),
|
|
307
|
+
);
|
|
308
|
+
const result = await fetchAndParseWorkflows({
|
|
309
|
+
...baseContext,
|
|
310
|
+
ref: 'main',
|
|
311
|
+
paths: [brokenTriggerPath, validTriggerPath],
|
|
312
|
+
sourceControl: sourceControl({fetchFile}),
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
expect(result).toHaveLength(2);
|
|
316
|
+
expect(result[0]?.diagnostics).toEqual([
|
|
317
|
+
{
|
|
318
|
+
code: 'invalid-cron-schedule',
|
|
319
|
+
message: 'Cron trigger schedule must be a valid 5-field cron expression.',
|
|
320
|
+
path: 'triggers.nightly.config.schedule',
|
|
321
|
+
severity: 'error',
|
|
322
|
+
},
|
|
323
|
+
]);
|
|
324
|
+
// The broken cron trigger is inert; the manual trigger stays active.
|
|
325
|
+
expect(result[0]?.definition.model.triggers.map((trigger) => trigger.key)).toEqual([
|
|
326
|
+
'on_demand',
|
|
327
|
+
]);
|
|
328
|
+
expect(result[1]?.path).toBe(validTriggerPath);
|
|
329
|
+
expect(result[1]?.diagnostics).toEqual([]);
|
|
330
|
+
expect(result[1]?.definition.model.jobs[0]?.steps).toHaveLength(1);
|
|
331
|
+
});
|
|
332
|
+
|
|
274
333
|
it('produces stable content hashes for identical content', async () => {
|
|
275
334
|
const sourceControlA = sourceControl();
|
|
276
335
|
const sourceControlB = sourceControl();
|
|
@@ -5,12 +5,12 @@ import {isInterModuleKnownError} from '@shipfox/inter-module';
|
|
|
5
5
|
import {boundedMap} from '@shipfox/node-module';
|
|
6
6
|
import type {IntegrationValidationContext} from './entities/integration-context.js';
|
|
7
7
|
import type {DefinitionSyncErrorCode} from './entities/sync-state.js';
|
|
8
|
-
import type {
|
|
8
|
+
import type {ValidationDiagnostic} from './entities/validation-diagnostic.js';
|
|
9
9
|
import type {WorkflowDefinitionPayload} from './entities/workflow-definition.js';
|
|
10
10
|
import {DefinitionParseError, DefinitionSyncPermanentError} from './errors.js';
|
|
11
11
|
import {hasAgentStepIntegrations} from './has-agent-step-integrations.js';
|
|
12
12
|
import type {DefinitionsSourceControl} from './integrations.js';
|
|
13
|
-
import {
|
|
13
|
+
import {parseDefinitionWithDiagnostics, stripDefinitionDiagnostics} from './parse-definition.js';
|
|
14
14
|
|
|
15
15
|
export const DEFAULT_WORKFLOW_PATH = '.shipfox/workflows/';
|
|
16
16
|
export const MAX_WORKFLOW_FILES = 100;
|
|
@@ -80,7 +80,7 @@ export interface ParsedWorkflow {
|
|
|
80
80
|
name: string;
|
|
81
81
|
definition: WorkflowDefinitionPayload;
|
|
82
82
|
contentHash: string;
|
|
83
|
-
|
|
83
|
+
diagnostics: ValidationDiagnostic[];
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
export interface FetchAndParseWorkflowsParams extends SyncSourceContext {
|
|
@@ -154,10 +154,10 @@ function parseWorkflowSnapshot(params: {
|
|
|
154
154
|
try {
|
|
155
155
|
const definition =
|
|
156
156
|
params.integrationValidationContext === undefined
|
|
157
|
-
?
|
|
157
|
+
? parseDefinitionWithDiagnostics(params.content, {
|
|
158
158
|
agentValidationCatalog: params.agentValidationCatalog,
|
|
159
159
|
})
|
|
160
|
-
:
|
|
160
|
+
: parseDefinitionWithDiagnostics(params.content, {
|
|
161
161
|
agentValidationCatalog: params.agentValidationCatalog,
|
|
162
162
|
integrationValidationContext: params.integrationValidationContext,
|
|
163
163
|
});
|
|
@@ -165,9 +165,9 @@ function parseWorkflowSnapshot(params: {
|
|
|
165
165
|
return {
|
|
166
166
|
path: params.path,
|
|
167
167
|
name: definition.document.name,
|
|
168
|
-
definition:
|
|
168
|
+
definition: stripDefinitionDiagnostics(definition),
|
|
169
169
|
contentHash,
|
|
170
|
-
|
|
170
|
+
diagnostics: definition.diagnostics,
|
|
171
171
|
};
|
|
172
172
|
} catch (error) {
|
|
173
173
|
if (error instanceof DefinitionParseError) {
|