@shipfox/api-definitions 11.0.0 → 12.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 +13 -12
- package/CHANGELOG.md +81 -0
- package/dist/core/entities/index.d.ts +1 -0
- package/dist/core/entities/index.d.ts.map +1 -1
- package/dist/core/entities/index.js +1 -0
- package/dist/core/entities/index.js.map +1 -1
- package/dist/core/entities/sync-state.d.ts +4 -0
- package/dist/core/entities/sync-state.d.ts.map +1 -1
- package/dist/core/entities/sync-state.js +10 -0
- package/dist/core/entities/sync-state.js.map +1 -1
- package/dist/core/entities/validation-warning.d.ts +6 -0
- package/dist/core/entities/validation-warning.d.ts.map +1 -0
- package/dist/core/entities/validation-warning.js +3 -0
- package/dist/core/entities/validation-warning.js.map +1 -0
- package/dist/core/parse-definition.d.ts +9 -7
- package/dist/core/parse-definition.d.ts.map +1 -1
- package/dist/core/parse-definition.js +14 -2
- package/dist/core/parse-definition.js.map +1 -1
- package/dist/core/sync-definitions.d.ts +2 -0
- package/dist/core/sync-definitions.d.ts.map +1 -1
- package/dist/core/sync-definitions.js +6 -5
- package/dist/core/sync-definitions.js.map +1 -1
- package/dist/core/validate-definition.d.ts +9 -5
- package/dist/core/validate-definition.d.ts.map +1 -1
- package/dist/core/validate-definition.js +23 -2
- package/dist/core/validate-definition.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 +3 -1
- 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-if-condition.d.ts +0 -1
- package/dist/core/workflow-model/normalize-if-condition.d.ts.map +1 -1
- package/dist/core/workflow-model/normalize-if-condition.js +0 -1
- package/dist/core/workflow-model/normalize-if-condition.js.map +1 -1
- package/dist/core/workflow-model/normalize-job-checkout.d.ts +15 -3
- package/dist/core/workflow-model/normalize-job-checkout.d.ts.map +1 -1
- package/dist/core/workflow-model/normalize-job-checkout.js +83 -4
- package/dist/core/workflow-model/normalize-job-checkout.js.map +1 -1
- package/dist/core/workflow-model/normalize-job-listening.js +0 -1
- package/dist/core/workflow-model/normalize-job-listening.js.map +1 -1
- package/dist/core/workflow-model/normalize-job-success.d.ts.map +1 -1
- package/dist/core/workflow-model/normalize-job-success.js +0 -1
- package/dist/core/workflow-model/normalize-job-success.js.map +1 -1
- package/dist/core/workflow-model/normalize-jobs.d.ts.map +1 -1
- package/dist/core/workflow-model/normalize-jobs.js +310 -45
- package/dist/core/workflow-model/normalize-jobs.js.map +1 -1
- package/dist/core/workflow-model/normalize-step-gate.js +0 -1
- package/dist/core/workflow-model/normalize-step-gate.js.map +1 -1
- package/dist/core/workflow-model/normalize-triggers.d.ts.map +1 -1
- package/dist/core/workflow-model/normalize-triggers.js +0 -1
- package/dist/core/workflow-model/normalize-triggers.js.map +1 -1
- package/dist/core/workflow-model/normalize-workflow-document.d.ts +3 -0
- package/dist/core/workflow-model/normalize-workflow-document.d.ts.map +1 -1
- package/dist/core/workflow-model/normalize-workflow-document.js +34 -4
- package/dist/core/workflow-model/normalize-workflow-document.js.map +1 -1
- package/dist/core/workflow-model/parse-interpolation-field.d.ts +1 -1
- package/dist/core/workflow-model/parse-interpolation-field.d.ts.map +1 -1
- package/dist/core/workflow-model/parse-interpolation-field.js +27 -57
- package/dist/core/workflow-model/parse-interpolation-field.js.map +1 -1
- package/dist/core/workflow-model/validate-literal-name.d.ts +12 -0
- package/dist/core/workflow-model/validate-literal-name.d.ts.map +1 -0
- package/dist/core/workflow-model/validate-literal-name.js +36 -0
- package/dist/core/workflow-model/validate-literal-name.js.map +1 -0
- package/dist/core/workflow-model/validate-predicate-expression.d.ts +1 -2
- package/dist/core/workflow-model/validate-predicate-expression.d.ts.map +1 -1
- package/dist/core/workflow-model/validate-predicate-expression.js +31 -90
- 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 +8 -2
- package/dist/core/workflow-model/validation-issue.js.map +1 -1
- package/dist/core/workflow-model/workflow-field-label.d.ts.map +1 -1
- package/dist/core/workflow-model/workflow-field-label.js +16 -2
- package/dist/core/workflow-model/workflow-field-label.js.map +1 -1
- package/dist/db/db.d.ts +38 -0
- package/dist/db/db.d.ts.map +1 -1
- package/dist/db/schema/sync-states.d.ts +19 -0
- package/dist/db/schema/sync-states.d.ts.map +1 -1
- package/dist/db/schema/sync-states.js +3 -1
- package/dist/db/schema/sync-states.js.map +1 -1
- package/dist/db/sync-states.d.ts +2 -1
- package/dist/db/sync-states.d.ts.map +1 -1
- package/dist/db/sync-states.js +4 -0
- package/dist/db/sync-states.js.map +1 -1
- package/dist/presentation/dto/definition.d.ts.map +1 -1
- package/dist/presentation/dto/definition.js +2 -1
- package/dist/presentation/dto/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.d.ts.map +1 -1
- package/dist/presentation/routes/validate-definition.js +5 -3
- package/dist/presentation/routes/validate-definition.js.map +1 -1
- package/dist/temporal/activities/sync-activities.d.ts +6 -2
- package/dist/temporal/activities/sync-activities.d.ts.map +1 -1
- package/dist/temporal/activities/sync-activities.js +8 -1
- package/dist/temporal/activities/sync-activities.js.map +1 -1
- package/dist/temporal/workflows/definition-sync.js +4 -1
- package/dist/temporal/workflows/definition-sync.js.map +1 -1
- package/dist/temporal/workflows/index.bundle.js +9029 -3
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/drizzle/0002_lumpy_ben_parker.sql +1 -0
- package/drizzle/meta/0002_snapshot.json +534 -0
- package/drizzle/meta/_journal.json +7 -0
- package/package.json +14 -14
- package/src/core/entities/index.ts +1 -0
- package/src/core/entities/sync-state.ts +23 -0
- package/src/core/entities/validation-warning.ts +5 -0
- package/src/core/parse-definition-warnings.test.ts +33 -0
- package/src/core/parse-definition.test.ts +7 -1
- package/src/core/parse-definition.ts +28 -10
- package/src/core/sync-definitions.test.ts +39 -0
- package/src/core/sync-definitions.ts +14 -4
- package/src/core/validate-definition.test.ts +128 -0
- package/src/core/validate-definition.ts +39 -8
- package/src/core/workflow-model/README.md +2 -2
- package/src/core/workflow-model/index.ts +1 -0
- package/src/core/workflow-model/invalid-workflow-model-error.ts +8 -2
- package/src/core/workflow-model/normalize-if-condition.ts +0 -2
- package/src/core/workflow-model/normalize-job-checkout.ts +105 -8
- package/src/core/workflow-model/normalize-job-listening.ts +0 -1
- package/src/core/workflow-model/normalize-job-success.ts +0 -1
- package/src/core/workflow-model/normalize-jobs.ts +330 -49
- package/src/core/workflow-model/normalize-step-gate.ts +0 -1
- package/src/core/workflow-model/normalize-triggers.ts +0 -1
- package/src/core/workflow-model/normalize-workflow-document.test.ts +732 -185
- package/src/core/workflow-model/normalize-workflow-document.ts +29 -4
- package/src/core/workflow-model/parse-interpolation-field.ts +50 -79
- package/src/core/workflow-model/validate-literal-name.ts +64 -0
- package/src/core/workflow-model/validate-predicate-expression.test.ts +153 -15
- package/src/core/workflow-model/validate-predicate-expression.ts +52 -120
- package/src/core/workflow-model/validation-issue.ts +4 -0
- package/src/core/workflow-model/workflow-field-label.ts +16 -2
- package/src/core/workflow-yaml/README.md +2 -2
- package/src/db/schema/sync-states.ts +4 -1
- package/src/db/sync-states.test.ts +57 -0
- package/src/db/sync-states.ts +6 -0
- package/src/presentation/dto/definition.ts +1 -0
- package/src/presentation/routes/list-definitions.test.ts +1 -0
- package/src/presentation/routes/validate-definition.test.ts +1 -0
- package/src/presentation/routes/validate-definition.ts +7 -1
- package/src/temporal/activities/sync-activities.test.ts +16 -0
- package/src/temporal/activities/sync-activities.ts +18 -3
- package/src/temporal/workflows/definition-sync.ts +1 -1
- package/test/agent-validation-catalog.ts +17 -0
- package/test/fixtures/valid-listening-job.yml +2 -1
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -2,8 +2,13 @@ import type {AgentValidationCatalog} from '@shipfox/api-agent-dto/inter-module';
|
|
|
2
2
|
import {
|
|
3
3
|
type AvailabilitySite,
|
|
4
4
|
buildTypedRootsEnvironment,
|
|
5
|
+
classifyShellCodePosition,
|
|
5
6
|
type ExpressionType,
|
|
6
7
|
type ExpressionTypeEnvironment,
|
|
8
|
+
hoistPlannedRunCommand,
|
|
9
|
+
type ShellReevaluatingConstruct,
|
|
10
|
+
type UnsafeRunInterpolation,
|
|
11
|
+
UnsafeRunInterpolationError,
|
|
7
12
|
type WorkflowJobTypeOverlay,
|
|
8
13
|
type WorkflowStepTypeOverlay,
|
|
9
14
|
} from '@shipfox/expression';
|
|
@@ -24,17 +29,22 @@ import type {
|
|
|
24
29
|
WorkflowEnvTemplates,
|
|
25
30
|
WorkflowFieldTemplate,
|
|
26
31
|
WorkflowModelAgentStep,
|
|
32
|
+
WorkflowModelCheckoutStep,
|
|
27
33
|
WorkflowModelJob,
|
|
28
34
|
WorkflowModelRunStep,
|
|
29
35
|
WorkflowModelStep,
|
|
36
|
+
WorkflowModelStepCheckout,
|
|
30
37
|
WorkflowOutputTemplates,
|
|
31
38
|
WorkflowStepSourceLocationMap,
|
|
32
39
|
} from '../entities/workflow-model.js';
|
|
33
|
-
import type {
|
|
40
|
+
import type {
|
|
41
|
+
WorkflowModelValidationIssue,
|
|
42
|
+
WorkflowModelValidationIssuePathSegment,
|
|
43
|
+
} from './invalid-workflow-model-error.js';
|
|
34
44
|
import {normalizeAgentIntegrations} from './normalize-agent-integrations.js';
|
|
35
45
|
import {normalizeEnv} from './normalize-env.js';
|
|
36
46
|
import {normalizeIfCondition} from './normalize-if-condition.js';
|
|
37
|
-
import {normalizeJobCheckout} from './normalize-job-checkout.js';
|
|
47
|
+
import {normalizeCheckout, normalizeJobCheckout} from './normalize-job-checkout.js';
|
|
38
48
|
import {normalizeJobListening} from './normalize-job-listening.js';
|
|
39
49
|
import {normalizeJobSuccess} from './normalize-job-success.js';
|
|
40
50
|
import {normalizeNeeds} from './normalize-needs.js';
|
|
@@ -43,6 +53,7 @@ import {normalizeStepOutputs} from './normalize-step-outputs.js';
|
|
|
43
53
|
import {parseDurationMs} from './parse-duration-ms.js';
|
|
44
54
|
import {parseInterpolationField} from './parse-interpolation-field.js';
|
|
45
55
|
import {stableId} from './stable-id.js';
|
|
56
|
+
import {unescapeLiteralName, validateLiteralName} from './validate-literal-name.js';
|
|
46
57
|
import {issue} from './validation-issue.js';
|
|
47
58
|
|
|
48
59
|
export interface NormalizeContext {
|
|
@@ -178,6 +189,8 @@ function normalizeJob(params: {
|
|
|
178
189
|
sourceName: params.sourceName,
|
|
179
190
|
jobId: id,
|
|
180
191
|
job: params.job,
|
|
192
|
+
workflowEnvKeys: Object.keys(params.document.env ?? {}),
|
|
193
|
+
jobEnvKeys: Object.keys(params.job.env ?? {}),
|
|
181
194
|
issues: params.issues,
|
|
182
195
|
stepSourceLocations: params.stepSourceLocations,
|
|
183
196
|
fillSite: stepFillSite,
|
|
@@ -194,7 +207,9 @@ function normalizeJob(params: {
|
|
|
194
207
|
issues: params.issues,
|
|
195
208
|
defaultRunnerLabels: params.context.defaultRunnerLabels,
|
|
196
209
|
});
|
|
197
|
-
const checkout = normalizeJobCheckout(
|
|
210
|
+
const checkout = normalizeJobCheckout({
|
|
211
|
+
checkout: params.job.checkout,
|
|
212
|
+
});
|
|
198
213
|
const jobEnv = normalizeEnv({
|
|
199
214
|
env: params.job.env,
|
|
200
215
|
path: ['jobs', params.sourceName, 'env'],
|
|
@@ -212,7 +227,6 @@ function normalizeJob(params: {
|
|
|
212
227
|
const condition = normalizeIfCondition({
|
|
213
228
|
field: 'job.if',
|
|
214
229
|
source: params.job.if,
|
|
215
|
-
site: 'job-activation',
|
|
216
230
|
path: ['jobs', params.sourceName, 'if'],
|
|
217
231
|
invalidCode: 'invalid-job-if',
|
|
218
232
|
invalidMessage: 'Job if must be a valid wrapped CEL boolean expression.',
|
|
@@ -242,17 +256,29 @@ function normalizeJob(params: {
|
|
|
242
256
|
issues: params.issues,
|
|
243
257
|
allowedJobReferences,
|
|
244
258
|
});
|
|
245
|
-
|
|
246
|
-
|
|
259
|
+
if (params.job.name !== undefined) {
|
|
260
|
+
validateLiteralName({
|
|
261
|
+
field: 'job.name',
|
|
262
|
+
dynamicField: 'execution_name',
|
|
263
|
+
source: params.job.name,
|
|
264
|
+
path: ['jobs', params.sourceName, 'name'],
|
|
265
|
+
message: 'Job name must be literal. Move runtime interpolation to execution_name.',
|
|
266
|
+
issues: params.issues,
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
const name = params.job.name === undefined ? undefined : unescapeLiteralName(params.job.name);
|
|
270
|
+
const executionName =
|
|
271
|
+
params.job.execution_name === undefined
|
|
247
272
|
? undefined
|
|
248
273
|
: (parseInterpolationField({
|
|
249
|
-
field: 'job.
|
|
250
|
-
source: params.job.
|
|
251
|
-
path: ['jobs', params.sourceName, '
|
|
274
|
+
field: 'job.execution_name',
|
|
275
|
+
source: params.job.execution_name,
|
|
276
|
+
path: ['jobs', params.sourceName, 'execution_name'],
|
|
252
277
|
issues: params.issues,
|
|
278
|
+
fillSite: 'execution-creation',
|
|
253
279
|
allowedJobReferences,
|
|
254
280
|
typeOverlay: upstreamJobsTypeOverlay,
|
|
255
|
-
}) ?? [{kind: 'literal' as const, value: params.job.
|
|
281
|
+
}) ?? [{kind: 'literal' as const, value: params.job.execution_name}]);
|
|
256
282
|
|
|
257
283
|
return {
|
|
258
284
|
id,
|
|
@@ -268,6 +294,7 @@ function normalizeJob(params: {
|
|
|
268
294
|
...(executionTimeoutMs === undefined ? {} : {executionTimeoutMs}),
|
|
269
295
|
...(listening === undefined ? {} : {listening}),
|
|
270
296
|
...(name === undefined ? {} : {name}),
|
|
297
|
+
...(executionName === undefined ? {} : {executionName}),
|
|
271
298
|
...jobEnv,
|
|
272
299
|
dependencies,
|
|
273
300
|
steps,
|
|
@@ -434,6 +461,8 @@ function normalizeJobSteps(params: {
|
|
|
434
461
|
sourceName: string;
|
|
435
462
|
jobId: string;
|
|
436
463
|
job: WorkflowDocumentJob;
|
|
464
|
+
workflowEnvKeys: readonly string[];
|
|
465
|
+
jobEnvKeys: readonly string[];
|
|
437
466
|
issues: WorkflowModelValidationIssue[];
|
|
438
467
|
stepSourceLocations: WorkflowStepSourceLocationMap | undefined;
|
|
439
468
|
fillSite: AvailabilitySite;
|
|
@@ -445,13 +474,15 @@ function normalizeJobSteps(params: {
|
|
|
445
474
|
}): readonly WorkflowModelStep[] {
|
|
446
475
|
const usedStepIds = new Map<string, number>();
|
|
447
476
|
|
|
448
|
-
return params.job.steps.
|
|
449
|
-
normalizeStep({
|
|
477
|
+
return params.job.steps.flatMap((step, index) => {
|
|
478
|
+
const normalized = normalizeStep({
|
|
450
479
|
step,
|
|
451
480
|
index,
|
|
452
481
|
sourceName: params.sourceName,
|
|
453
482
|
jobId: params.jobId,
|
|
454
483
|
allSteps: params.job.steps,
|
|
484
|
+
workflowEnvKeys: params.workflowEnvKeys,
|
|
485
|
+
jobEnvKeys: params.jobEnvKeys,
|
|
455
486
|
usedStepIds,
|
|
456
487
|
issues: params.issues,
|
|
457
488
|
stepSourceLocations: params.stepSourceLocations,
|
|
@@ -461,8 +492,9 @@ function normalizeJobSteps(params: {
|
|
|
461
492
|
upstreamJobs: params.upstreamJobs,
|
|
462
493
|
directNeedJobs: params.directNeedJobs,
|
|
463
494
|
context: params.context,
|
|
464
|
-
})
|
|
465
|
-
|
|
495
|
+
});
|
|
496
|
+
return normalized === undefined ? [] : [normalized];
|
|
497
|
+
});
|
|
466
498
|
}
|
|
467
499
|
|
|
468
500
|
function normalizeStep(params: {
|
|
@@ -471,6 +503,8 @@ function normalizeStep(params: {
|
|
|
471
503
|
sourceName: string;
|
|
472
504
|
jobId: string;
|
|
473
505
|
allSteps: readonly WorkflowDocumentStep[];
|
|
506
|
+
workflowEnvKeys: readonly string[];
|
|
507
|
+
jobEnvKeys: readonly string[];
|
|
474
508
|
usedStepIds: Map<string, number>;
|
|
475
509
|
issues: WorkflowModelValidationIssue[];
|
|
476
510
|
stepSourceLocations: WorkflowStepSourceLocationMap | undefined;
|
|
@@ -480,7 +514,7 @@ function normalizeStep(params: {
|
|
|
480
514
|
upstreamJobs: readonly WorkflowJobTypeOverlay[];
|
|
481
515
|
directNeedJobs: readonly WorkflowJobTypeOverlay[];
|
|
482
516
|
context: NormalizeContext;
|
|
483
|
-
}): WorkflowModelStep {
|
|
517
|
+
}): WorkflowModelStep | undefined {
|
|
484
518
|
const stepKey = params.step.key;
|
|
485
519
|
const stepId =
|
|
486
520
|
stepKey === undefined
|
|
@@ -532,7 +566,6 @@ function normalizeStep(params: {
|
|
|
532
566
|
const condition = normalizeIfCondition({
|
|
533
567
|
field: 'step.if',
|
|
534
568
|
source: params.step.if,
|
|
535
|
-
site: 'step-dispatch',
|
|
536
569
|
path: ['jobs', params.sourceName, 'steps', params.index, 'if'],
|
|
537
570
|
invalidCode: 'invalid-step-if',
|
|
538
571
|
invalidMessage: 'Step if must be a valid wrapped CEL boolean expression.',
|
|
@@ -567,10 +600,25 @@ function normalizeStep(params: {
|
|
|
567
600
|
allowedJobReferences: params.allowedJobReferences,
|
|
568
601
|
typeOverlay,
|
|
569
602
|
});
|
|
603
|
+
const workingDirectory =
|
|
604
|
+
params.step.working_directory === undefined
|
|
605
|
+
? undefined
|
|
606
|
+
: parseInterpolationField({
|
|
607
|
+
field: 'step.working_directory',
|
|
608
|
+
source: params.step.working_directory,
|
|
609
|
+
path: ['jobs', params.sourceName, 'steps', params.index, 'working_directory'],
|
|
610
|
+
issues: params.issues,
|
|
611
|
+
fillSite: params.fillSite,
|
|
612
|
+
allowedJobReferences: params.allowedJobReferences,
|
|
613
|
+
typeOverlay,
|
|
614
|
+
});
|
|
570
615
|
const stepBase = {
|
|
571
616
|
id: stepId,
|
|
572
617
|
...(stepKey === undefined ? {} : {key: stepKey}),
|
|
573
618
|
...(params.step.name === undefined ? {} : {name: params.step.name}),
|
|
619
|
+
...(params.step.working_directory === undefined
|
|
620
|
+
? {}
|
|
621
|
+
: {workingDirectory: params.step.working_directory}),
|
|
574
622
|
...(outputs === undefined ? {} : {outputs}),
|
|
575
623
|
...(sourceLocation === undefined ? {} : {sourceLocation}),
|
|
576
624
|
...(condition === undefined ? {} : {if: condition}),
|
|
@@ -583,7 +631,10 @@ function normalizeStep(params: {
|
|
|
583
631
|
stepBase,
|
|
584
632
|
sourceName: params.sourceName,
|
|
585
633
|
stepIndex: params.index,
|
|
634
|
+
workflowEnvKeys: params.workflowEnvKeys,
|
|
635
|
+
jobEnvKeys: params.jobEnvKeys,
|
|
586
636
|
name,
|
|
637
|
+
workingDirectory,
|
|
587
638
|
issues: params.issues,
|
|
588
639
|
fillSite: params.fillSite,
|
|
589
640
|
allowedJobReferences: params.allowedJobReferences,
|
|
@@ -598,6 +649,7 @@ function normalizeStep(params: {
|
|
|
598
649
|
sourceName: params.sourceName,
|
|
599
650
|
stepIndex: params.index,
|
|
600
651
|
name,
|
|
652
|
+
workingDirectory,
|
|
601
653
|
issues: params.issues,
|
|
602
654
|
fillSite: params.fillSite,
|
|
603
655
|
allowedJobReferences: params.allowedJobReferences,
|
|
@@ -606,9 +658,55 @@ function normalizeStep(params: {
|
|
|
606
658
|
});
|
|
607
659
|
}
|
|
608
660
|
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
661
|
+
if (params.step.checkout !== undefined) {
|
|
662
|
+
return normalizeCheckoutStep({
|
|
663
|
+
step: params.step,
|
|
664
|
+
stepBase,
|
|
665
|
+
name,
|
|
666
|
+
sourceName: params.sourceName,
|
|
667
|
+
stepIndex: params.index,
|
|
668
|
+
issues: params.issues,
|
|
669
|
+
fillSite: params.fillSite,
|
|
670
|
+
allowedJobReferences: params.allowedJobReferences,
|
|
671
|
+
typeOverlay,
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
// Keep the model-step union honest if callers bypass the document parser.
|
|
676
|
+
throw new Error(`Workflow step "${stepId}" is neither a run, agent, nor checkout step`);
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
function normalizeCheckoutStep(params: {
|
|
680
|
+
step: WorkflowDocumentStep;
|
|
681
|
+
stepBase: WorkflowModelStepBaseFields;
|
|
682
|
+
name: WorkflowFieldTemplate | undefined;
|
|
683
|
+
sourceName: string;
|
|
684
|
+
stepIndex: number;
|
|
685
|
+
issues: WorkflowModelValidationIssue[];
|
|
686
|
+
fillSite: AvailabilitySite;
|
|
687
|
+
allowedJobReferences: ReadonlySet<string>;
|
|
688
|
+
typeOverlay?: ExpressionTypeEnvironment | undefined;
|
|
689
|
+
}): WorkflowModelCheckoutStep {
|
|
690
|
+
const checkout = params.step.checkout;
|
|
691
|
+
if (checkout === undefined) {
|
|
692
|
+
throw new Error('Checkout step normalization requires checkout settings');
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
const normalizedCheckout: WorkflowModelStepCheckout = normalizeCheckout({
|
|
696
|
+
checkout,
|
|
697
|
+
issues: params.issues,
|
|
698
|
+
path: ['jobs', params.sourceName, 'steps', params.stepIndex, 'checkout'],
|
|
699
|
+
fillSite: params.fillSite,
|
|
700
|
+
allowedJobReferences: params.allowedJobReferences,
|
|
701
|
+
typeOverlay: params.typeOverlay,
|
|
702
|
+
});
|
|
703
|
+
|
|
704
|
+
return {
|
|
705
|
+
...params.stepBase,
|
|
706
|
+
kind: 'checkout',
|
|
707
|
+
checkout: normalizedCheckout,
|
|
708
|
+
...(params.name === undefined ? {} : {templates: {name: params.name}}),
|
|
709
|
+
};
|
|
612
710
|
}
|
|
613
711
|
|
|
614
712
|
function normalizeRunStep(params: {
|
|
@@ -616,7 +714,10 @@ function normalizeRunStep(params: {
|
|
|
616
714
|
stepBase: WorkflowModelStepBaseFields;
|
|
617
715
|
sourceName: string;
|
|
618
716
|
stepIndex: number;
|
|
717
|
+
workflowEnvKeys: readonly string[];
|
|
718
|
+
jobEnvKeys: readonly string[];
|
|
619
719
|
name: WorkflowFieldTemplate | undefined;
|
|
720
|
+
workingDirectory: WorkflowFieldTemplate | undefined;
|
|
620
721
|
issues: WorkflowModelValidationIssue[];
|
|
621
722
|
fillSite: AvailabilitySite;
|
|
622
723
|
allowedJobReferences: ReadonlySet<string>;
|
|
@@ -643,9 +744,19 @@ function normalizeRunStep(params: {
|
|
|
643
744
|
allowedJobReferences: params.allowedJobReferences,
|
|
644
745
|
typeOverlay: params.typeOverlay,
|
|
645
746
|
});
|
|
747
|
+
addReevaluatingCommandWarnings({
|
|
748
|
+
command: commandTemplate,
|
|
749
|
+
source: params.step.run,
|
|
750
|
+
workflowEnvKeys: params.workflowEnvKeys,
|
|
751
|
+
jobEnvKeys: params.jobEnvKeys,
|
|
752
|
+
stepEnvKeys: Object.keys(params.step.env ?? {}),
|
|
753
|
+
path: ['jobs', params.sourceName, 'steps', params.stepIndex, 'run'],
|
|
754
|
+
issues: params.issues,
|
|
755
|
+
});
|
|
646
756
|
const templates = optionalRunStepTemplates({
|
|
647
757
|
command: commandTemplate,
|
|
648
758
|
name: params.name,
|
|
759
|
+
workingDirectory: params.workingDirectory,
|
|
649
760
|
env: stepEnv.templates?.env,
|
|
650
761
|
});
|
|
651
762
|
|
|
@@ -658,12 +769,110 @@ function normalizeRunStep(params: {
|
|
|
658
769
|
};
|
|
659
770
|
}
|
|
660
771
|
|
|
772
|
+
function addReevaluatingCommandWarnings(params: {
|
|
773
|
+
command: WorkflowFieldTemplate | undefined;
|
|
774
|
+
source: string;
|
|
775
|
+
workflowEnvKeys: readonly string[];
|
|
776
|
+
jobEnvKeys: readonly string[];
|
|
777
|
+
stepEnvKeys: readonly string[];
|
|
778
|
+
path: readonly WorkflowModelValidationIssuePathSegment[];
|
|
779
|
+
issues: WorkflowModelValidationIssue[];
|
|
780
|
+
}): void {
|
|
781
|
+
const envKeys = new Set([...params.workflowEnvKeys, ...params.jobEnvKeys, ...params.stepEnvKeys]);
|
|
782
|
+
let hoisted: ReturnType<typeof hoistPlannedRunCommand>;
|
|
783
|
+
|
|
784
|
+
try {
|
|
785
|
+
hoisted = hoistPlannedRunCommand({
|
|
786
|
+
field: {
|
|
787
|
+
segments: params.command ?? [{kind: 'literal', value: params.source}],
|
|
788
|
+
},
|
|
789
|
+
reservedNames: envKeys,
|
|
790
|
+
});
|
|
791
|
+
} catch (error) {
|
|
792
|
+
if (!(error instanceof UnsafeRunInterpolationError)) return;
|
|
793
|
+
|
|
794
|
+
for (const occurrence of error.occurrences) {
|
|
795
|
+
params.issues.push(
|
|
796
|
+
issue({
|
|
797
|
+
code: 're-evaluating-command',
|
|
798
|
+
message: unsafeInterpolationWarningMessage(occurrence),
|
|
799
|
+
path: params.path,
|
|
800
|
+
severity: 'warning',
|
|
801
|
+
}),
|
|
802
|
+
);
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
if (error.partial === undefined) return;
|
|
806
|
+
hoisted = error.partial;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
try {
|
|
810
|
+
const matches = classifyShellCodePosition({
|
|
811
|
+
command: hoisted.command,
|
|
812
|
+
workflowDataNames: [...envKeys, ...hoisted.bindings.map((binding) => binding.name)],
|
|
813
|
+
}).matches;
|
|
814
|
+
|
|
815
|
+
for (const match of matches) {
|
|
816
|
+
params.issues.push(
|
|
817
|
+
issue({
|
|
818
|
+
code: 're-evaluating-command',
|
|
819
|
+
message: reevaluatingCommandWarningMessage(match.construct, match.name),
|
|
820
|
+
path: params.path,
|
|
821
|
+
severity: 'warning',
|
|
822
|
+
}),
|
|
823
|
+
);
|
|
824
|
+
}
|
|
825
|
+
} catch {
|
|
826
|
+
return;
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
function unsafeInterpolationWarningMessage(error: UnsafeRunInterpolation): string {
|
|
831
|
+
const regionLabel = {
|
|
832
|
+
single: 'single-quoted shell text',
|
|
833
|
+
double: 'double-quoted shell text',
|
|
834
|
+
'dollar-single': 'ANSI-C shell text',
|
|
835
|
+
'dollar-double': 'dollar-double-quoted shell text',
|
|
836
|
+
'paren-sub': 'command substitution',
|
|
837
|
+
arith: 'shell arithmetic',
|
|
838
|
+
backtick: 'backtick substitution',
|
|
839
|
+
'param-brace': 'shell parameter expansion',
|
|
840
|
+
heredoc: 'a heredoc',
|
|
841
|
+
'line-comment': 'a shell comment',
|
|
842
|
+
escape: 'a shell escape',
|
|
843
|
+
}[error.region];
|
|
844
|
+
|
|
845
|
+
return `Value "${error.source}" is inside ${regionLabel} and will be re-executed as code. Hoisting cannot protect it; move the interpolation to a normal quoted argument instead.`;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
function reevaluatingCommandWarningMessage(
|
|
849
|
+
construct: ShellReevaluatingConstruct,
|
|
850
|
+
name: string,
|
|
851
|
+
): string {
|
|
852
|
+
const constructLabel = {
|
|
853
|
+
eval: 'eval',
|
|
854
|
+
'sh-c': 'sh -c',
|
|
855
|
+
'bash-c': 'bash -c',
|
|
856
|
+
source: 'source',
|
|
857
|
+
let: 'let',
|
|
858
|
+
'declare-i': 'declare -i',
|
|
859
|
+
arithmetic: 'shell arithmetic',
|
|
860
|
+
awk: 'awk',
|
|
861
|
+
jq: 'jq',
|
|
862
|
+
sed: 'sed',
|
|
863
|
+
'xargs-sh-c': 'xargs sh -c',
|
|
864
|
+
}[construct];
|
|
865
|
+
|
|
866
|
+
return `Value "$${name}" is passed to ${constructLabel} and re-executed as code. Hoisting cannot protect it; pass the value to a fixed program instead.`;
|
|
867
|
+
}
|
|
868
|
+
|
|
661
869
|
function normalizeAgentStep(params: {
|
|
662
870
|
step: WorkflowDocumentStep;
|
|
663
871
|
stepBase: WorkflowModelStepBaseFields;
|
|
664
872
|
sourceName: string;
|
|
665
873
|
stepIndex: number;
|
|
666
874
|
name: WorkflowFieldTemplate | undefined;
|
|
875
|
+
workingDirectory: WorkflowFieldTemplate | undefined;
|
|
667
876
|
issues: WorkflowModelValidationIssue[];
|
|
668
877
|
fillSite: AvailabilitySite;
|
|
669
878
|
allowedJobReferences: ReadonlySet<string>;
|
|
@@ -707,12 +916,27 @@ function normalizeAgentStep(params: {
|
|
|
707
916
|
allowedJobReferences: params.allowedJobReferences,
|
|
708
917
|
typeOverlay: params.typeOverlay,
|
|
709
918
|
});
|
|
919
|
+
const thinkingTemplate =
|
|
920
|
+
params.step.thinking === undefined
|
|
921
|
+
? undefined
|
|
922
|
+
: parseInterpolationField({
|
|
923
|
+
field: 'agent.thinking',
|
|
924
|
+
source: params.step.thinking,
|
|
925
|
+
path: ['jobs', params.sourceName, 'steps', params.stepIndex, 'thinking'],
|
|
926
|
+
issues: params.issues,
|
|
927
|
+
fillSite: params.fillSite,
|
|
928
|
+
allowedJobReferences: params.allowedJobReferences,
|
|
929
|
+
typeOverlay: params.typeOverlay,
|
|
930
|
+
});
|
|
710
931
|
validateAgentStep({
|
|
711
932
|
step: params.step,
|
|
712
933
|
sourceName: params.sourceName,
|
|
713
934
|
stepIndex: params.stepIndex,
|
|
714
935
|
issues: params.issues,
|
|
715
|
-
|
|
936
|
+
validateLiteralModel:
|
|
937
|
+
params.step.model !== undefined && !hasInterpolationSyntax(params.step.model),
|
|
938
|
+
validateLiteralProvider:
|
|
939
|
+
params.step.provider !== undefined && !hasInterpolationSyntax(params.step.provider),
|
|
716
940
|
agentValidationCatalog: params.context.agentValidationCatalog,
|
|
717
941
|
});
|
|
718
942
|
const integrations = normalizeAgentIntegrations({
|
|
@@ -726,7 +950,9 @@ function normalizeAgentStep(params: {
|
|
|
726
950
|
prompt: promptTemplate,
|
|
727
951
|
model: modelTemplate,
|
|
728
952
|
provider: providerTemplate,
|
|
953
|
+
thinking: thinkingTemplate,
|
|
729
954
|
name: params.name,
|
|
955
|
+
workingDirectory: params.workingDirectory,
|
|
730
956
|
});
|
|
731
957
|
|
|
732
958
|
return {
|
|
@@ -748,47 +974,83 @@ function validateAgentStep(params: {
|
|
|
748
974
|
sourceName: string;
|
|
749
975
|
stepIndex: number;
|
|
750
976
|
issues: WorkflowModelValidationIssue[];
|
|
977
|
+
validateLiteralModel: boolean;
|
|
751
978
|
validateLiteralProvider: boolean;
|
|
752
979
|
agentValidationCatalog: AgentValidationCatalog;
|
|
753
980
|
}): void {
|
|
754
981
|
validateHarnessThinking(params);
|
|
755
982
|
validateHarnessTools(params);
|
|
756
|
-
if (!params.validateLiteralProvider) return;
|
|
757
|
-
|
|
758
983
|
const providerId = params.step.provider;
|
|
759
|
-
if (providerId === undefined) return;
|
|
760
|
-
|
|
761
|
-
const provider = params.agentValidationCatalog.providers.find((entry) => entry.id === providerId);
|
|
762
984
|
const harness = params.step.harness;
|
|
763
|
-
|
|
985
|
+
const provider =
|
|
986
|
+
providerId === undefined
|
|
987
|
+
? undefined
|
|
988
|
+
: params.agentValidationCatalog.providers.find((entry) => entry.id === providerId);
|
|
989
|
+
|
|
990
|
+
if (params.validateLiteralProvider && providerId !== undefined) {
|
|
991
|
+
if (provider === undefined) {
|
|
992
|
+
if (harness === undefined || harness === 'pi') return;
|
|
993
|
+
params.issues.push(
|
|
994
|
+
issue({
|
|
995
|
+
code: 'invalid-provider',
|
|
996
|
+
message: `Provider "${providerId}" is not supported.`,
|
|
997
|
+
path: ['jobs', params.sourceName, 'steps', params.stepIndex, 'provider'],
|
|
998
|
+
details: {provider: providerId},
|
|
999
|
+
}),
|
|
1000
|
+
);
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
764
1003
|
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
1004
|
+
if (provider.support_status !== 'supported') {
|
|
1005
|
+
params.issues.push(
|
|
1006
|
+
issue({
|
|
1007
|
+
code: 'invalid-provider',
|
|
1008
|
+
message: `Provider "${providerId}" is not supported.`,
|
|
1009
|
+
path: ['jobs', params.sourceName, 'steps', params.stepIndex, 'provider'],
|
|
1010
|
+
details: {provider: providerId},
|
|
1011
|
+
}),
|
|
1012
|
+
);
|
|
1013
|
+
return;
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
if (harness === undefined) return;
|
|
1017
|
+
|
|
1018
|
+
const descriptor = params.agentValidationCatalog.harnesses.find(
|
|
1019
|
+
(entry) => entry.id === harness,
|
|
773
1020
|
);
|
|
774
|
-
|
|
1021
|
+
if (!descriptor?.supported_provider_ids.includes(providerId)) {
|
|
1022
|
+
params.issues.push(
|
|
1023
|
+
issue({
|
|
1024
|
+
code: 'harness-provider-incompatible',
|
|
1025
|
+
message: `Harness "${harness}" does not support provider: ${providerId}. Supported providers: ${descriptor?.supported_provider_ids.join(', ') ?? ''}.`,
|
|
1026
|
+
path: ['jobs', params.sourceName, 'steps', params.stepIndex, 'provider'],
|
|
1027
|
+
details: {
|
|
1028
|
+
harness,
|
|
1029
|
+
provider: providerId,
|
|
1030
|
+
supportedProviders: descriptor?.supported_provider_ids ?? [],
|
|
1031
|
+
},
|
|
1032
|
+
}),
|
|
1033
|
+
);
|
|
1034
|
+
return;
|
|
1035
|
+
}
|
|
775
1036
|
}
|
|
776
1037
|
|
|
777
|
-
if (harness === undefined) return;
|
|
1038
|
+
if (!params.validateLiteralModel || providerId === undefined || harness === undefined) return;
|
|
1039
|
+
if (provider === undefined || provider.support_status !== 'supported') return;
|
|
1040
|
+
|
|
1041
|
+
const model = params.step.model;
|
|
1042
|
+
if (model === undefined) return;
|
|
778
1043
|
|
|
779
1044
|
const descriptor = params.agentValidationCatalog.harnesses.find((entry) => entry.id === harness);
|
|
780
|
-
|
|
1045
|
+
const modelIds = descriptor?.model_ids_by_provider?.[providerId];
|
|
1046
|
+
if (modelIds === undefined || modelIds.includes(model)) return;
|
|
781
1047
|
|
|
782
1048
|
params.issues.push(
|
|
783
1049
|
issue({
|
|
784
|
-
code: '
|
|
785
|
-
message: `
|
|
786
|
-
path: ['jobs', params.sourceName, 'steps', params.stepIndex, '
|
|
787
|
-
details: {
|
|
788
|
-
harness,
|
|
789
|
-
provider: providerId,
|
|
790
|
-
supportedProviders: descriptor?.supported_provider_ids ?? [],
|
|
791
|
-
},
|
|
1050
|
+
code: 'invalid-model',
|
|
1051
|
+
message: `Agent model "${model}" is not available for harness "${harness}" and provider "${providerId}".`,
|
|
1052
|
+
path: ['jobs', params.sourceName, 'steps', params.stepIndex, 'model'],
|
|
1053
|
+
details: {harness, provider: providerId, model},
|
|
792
1054
|
}),
|
|
793
1055
|
);
|
|
794
1056
|
}
|
|
@@ -844,11 +1106,14 @@ function validateHarnessThinking(params: {
|
|
|
844
1106
|
}): void {
|
|
845
1107
|
const {harness, thinking} = params.step;
|
|
846
1108
|
if (harness === undefined || thinking === undefined) return;
|
|
1109
|
+
// An interpolated level is only known when the step dispatches, so the agent
|
|
1110
|
+
// module checks it against the resolved harness there.
|
|
1111
|
+
if (hasInterpolationSyntax(thinking)) return;
|
|
847
1112
|
|
|
848
1113
|
const supportedLevels =
|
|
849
1114
|
params.agentValidationCatalog.harnesses.find((entry) => entry.id === harness)
|
|
850
1115
|
?.thinking_levels ?? [];
|
|
851
|
-
if (supportedLevels.includes(thinking)) return;
|
|
1116
|
+
if ((supportedLevels as readonly string[]).includes(thinking)) return;
|
|
852
1117
|
params.issues.push(
|
|
853
1118
|
issue({
|
|
854
1119
|
code: 'harness-thinking-incompatible',
|
|
@@ -953,27 +1218,35 @@ function validateRunnerLabels(params: {
|
|
|
953
1218
|
|
|
954
1219
|
type WorkflowModelStepBaseFields = Pick<
|
|
955
1220
|
WorkflowModelStep,
|
|
956
|
-
'id' | 'key' | 'name' | 'outputs' | 'sourceLocation' | 'gate'
|
|
1221
|
+
'id' | 'key' | 'name' | 'workingDirectory' | 'outputs' | 'sourceLocation' | 'gate'
|
|
957
1222
|
>;
|
|
958
1223
|
|
|
959
1224
|
function optionalRunStepTemplates(params: {
|
|
960
1225
|
command: WorkflowFieldTemplate | undefined;
|
|
961
1226
|
name: WorkflowFieldTemplate | undefined;
|
|
1227
|
+
workingDirectory: WorkflowFieldTemplate | undefined;
|
|
962
1228
|
env: WorkflowEnvTemplates | undefined;
|
|
963
1229
|
}):
|
|
964
1230
|
| {
|
|
965
1231
|
command?: WorkflowFieldTemplate;
|
|
966
1232
|
name?: WorkflowFieldTemplate;
|
|
1233
|
+
workingDirectory?: WorkflowFieldTemplate;
|
|
967
1234
|
env?: WorkflowEnvTemplates;
|
|
968
1235
|
}
|
|
969
1236
|
| undefined {
|
|
970
|
-
if (
|
|
1237
|
+
if (
|
|
1238
|
+
params.command === undefined &&
|
|
1239
|
+
params.name === undefined &&
|
|
1240
|
+
params.workingDirectory === undefined &&
|
|
1241
|
+
params.env === undefined
|
|
1242
|
+
) {
|
|
971
1243
|
return undefined;
|
|
972
1244
|
}
|
|
973
1245
|
|
|
974
1246
|
return {
|
|
975
1247
|
...(params.command === undefined ? {} : {command: params.command}),
|
|
976
1248
|
...(params.name === undefined ? {} : {name: params.name}),
|
|
1249
|
+
...(params.workingDirectory === undefined ? {} : {workingDirectory: params.workingDirectory}),
|
|
977
1250
|
...(params.env === undefined ? {} : {env: params.env}),
|
|
978
1251
|
};
|
|
979
1252
|
}
|
|
@@ -982,20 +1255,26 @@ function optionalAgentStepTemplates(params: {
|
|
|
982
1255
|
prompt: WorkflowFieldTemplate | undefined;
|
|
983
1256
|
model: WorkflowFieldTemplate | undefined;
|
|
984
1257
|
provider: WorkflowFieldTemplate | undefined;
|
|
1258
|
+
thinking: WorkflowFieldTemplate | undefined;
|
|
985
1259
|
name: WorkflowFieldTemplate | undefined;
|
|
1260
|
+
workingDirectory: WorkflowFieldTemplate | undefined;
|
|
986
1261
|
}):
|
|
987
1262
|
| {
|
|
988
1263
|
prompt?: WorkflowFieldTemplate;
|
|
989
1264
|
model?: WorkflowFieldTemplate;
|
|
990
1265
|
provider?: WorkflowFieldTemplate;
|
|
1266
|
+
thinking?: WorkflowFieldTemplate;
|
|
991
1267
|
name?: WorkflowFieldTemplate;
|
|
1268
|
+
workingDirectory?: WorkflowFieldTemplate;
|
|
992
1269
|
}
|
|
993
1270
|
| undefined {
|
|
994
1271
|
if (
|
|
995
1272
|
params.prompt === undefined &&
|
|
996
1273
|
params.model === undefined &&
|
|
997
1274
|
params.provider === undefined &&
|
|
998
|
-
params.
|
|
1275
|
+
params.thinking === undefined &&
|
|
1276
|
+
params.name === undefined &&
|
|
1277
|
+
params.workingDirectory === undefined
|
|
999
1278
|
) {
|
|
1000
1279
|
return undefined;
|
|
1001
1280
|
}
|
|
@@ -1004,6 +1283,8 @@ function optionalAgentStepTemplates(params: {
|
|
|
1004
1283
|
...(params.prompt === undefined ? {} : {prompt: params.prompt}),
|
|
1005
1284
|
...(params.model === undefined ? {} : {model: params.model}),
|
|
1006
1285
|
...(params.provider === undefined ? {} : {provider: params.provider}),
|
|
1286
|
+
...(params.thinking === undefined ? {} : {thinking: params.thinking}),
|
|
1007
1287
|
...(params.name === undefined ? {} : {name: params.name}),
|
|
1288
|
+
...(params.workingDirectory === undefined ? {} : {workingDirectory: params.workingDirectory}),
|
|
1008
1289
|
};
|
|
1009
1290
|
}
|
|
@@ -79,7 +79,6 @@ function normalizeGateSuccess(params: {
|
|
|
79
79
|
return validatePredicateExpression({
|
|
80
80
|
field: 'step.success',
|
|
81
81
|
source: params.source,
|
|
82
|
-
site: 'step-report',
|
|
83
82
|
path: ['jobs', params.sourceName, 'steps', params.stepIndex, 'gate', 'success'],
|
|
84
83
|
invalidCode: 'invalid-step-gate-success',
|
|
85
84
|
invalidMessage: 'Step gate success must be a valid CEL boolean expression.',
|
|
@@ -107,7 +107,6 @@ function validateTriggerFilter(params: {
|
|
|
107
107
|
validatePredicateExpression({
|
|
108
108
|
field: 'trigger.filter',
|
|
109
109
|
source: trigger.filter,
|
|
110
|
-
site: 'ingest',
|
|
111
110
|
path,
|
|
112
111
|
invalidCode: 'invalid-trigger-filter',
|
|
113
112
|
invalidMessage: 'Trigger filter must be a valid boolean predicate.',
|