@shipfox/api-definitions 11.0.0 → 12.2.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 +106 -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 +5 -1
- 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/errors.d.ts +2 -2
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/integrations.d.ts +1 -1
- 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 +315 -74
- 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/core/workflow-yaml/invalid-workflow-yaml-error.d.ts.map +1 -1
- package/dist/db/db.d.ts +48 -10
- package/dist/db/db.d.ts.map +1 -1
- package/dist/db/schema/definitions.d.ts +1 -1
- package/dist/db/schema/outbox.d.ts +1 -1
- package/dist/db/schema/sync-states.d.ts +22 -3
- 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/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.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/presentation/subscribers/index.d.ts +1 -0
- package/dist/presentation/subscribers/index.d.ts.map +1 -1
- package/dist/presentation/subscribers/index.js +1 -0
- package/dist/presentation/subscribers/index.js.map +1 -1
- package/dist/presentation/subscribers/on-integration-connection-available.d.ts +5 -0
- package/dist/presentation/subscribers/on-integration-connection-available.d.ts.map +1 -0
- package/dist/presentation/subscribers/on-integration-connection-available.js +32 -0
- package/dist/presentation/subscribers/on-integration-connection-available.js.map +1 -0
- package/dist/presentation/subscribers/start-definition-sync.d.ts +1 -0
- package/dist/presentation/subscribers/start-definition-sync.d.ts.map +1 -1
- package/dist/presentation/subscribers/start-definition-sync.js +30 -17
- package/dist/presentation/subscribers/start-definition-sync.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 +17 -17
- 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 -3
- 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 +335 -90
- 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 +876 -202
- 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/index.ts +12 -1
- 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/presentation/subscribers/index.ts +1 -0
- package/src/presentation/subscribers/on-integration-connection-available.test.ts +132 -0
- package/src/presentation/subscribers/on-integration-connection-available.ts +44 -0
- package/src/presentation/subscribers/start-definition-sync.ts +35 -19
- 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
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { buildTypedRootsEnvironment } from '@shipfox/expression';
|
|
1
|
+
import { buildTypedRootsEnvironment, classifyShellCodePosition, hoistPlannedRunCommand, UnsafeRunInterpolationError } from '@shipfox/expression';
|
|
2
2
|
import { canonicalizeLabels, findInvalidLabels, MAX_RUNNER_LABEL_LENGTH, MAX_RUNNER_LABELS, RUNNER_LABEL_PATTERN } from '@shipfox/runner-labels';
|
|
3
3
|
import { normalizeAgentIntegrations } from './normalize-agent-integrations.js';
|
|
4
4
|
import { normalizeEnv } from './normalize-env.js';
|
|
5
5
|
import { normalizeIfCondition } from './normalize-if-condition.js';
|
|
6
|
-
import { normalizeJobCheckout } from './normalize-job-checkout.js';
|
|
6
|
+
import { normalizeCheckout, normalizeJobCheckout } from './normalize-job-checkout.js';
|
|
7
7
|
import { normalizeJobListening } from './normalize-job-listening.js';
|
|
8
8
|
import { normalizeJobSuccess } from './normalize-job-success.js';
|
|
9
9
|
import { normalizeNeeds } from './normalize-needs.js';
|
|
@@ -12,6 +12,7 @@ import { normalizeStepOutputs } from './normalize-step-outputs.js';
|
|
|
12
12
|
import { parseDurationMs } from './parse-duration-ms.js';
|
|
13
13
|
import { parseInterpolationField } from './parse-interpolation-field.js';
|
|
14
14
|
import { stableId } from './stable-id.js';
|
|
15
|
+
import { unescapeLiteralName, validateLiteralName } from './validate-literal-name.js';
|
|
15
16
|
import { issue } from './validation-issue.js';
|
|
16
17
|
export function normalizeJobs(document, jobIdBySourceName, issues, stepSourceLocations, context) {
|
|
17
18
|
const entries = Object.entries(document.jobs);
|
|
@@ -109,6 +110,8 @@ function normalizeJob(params) {
|
|
|
109
110
|
sourceName: params.sourceName,
|
|
110
111
|
jobId: id,
|
|
111
112
|
job: params.job,
|
|
113
|
+
workflowEnvKeys: Object.keys(params.document.env ?? {}),
|
|
114
|
+
jobEnvKeys: Object.keys(params.job.env ?? {}),
|
|
112
115
|
issues: params.issues,
|
|
113
116
|
stepSourceLocations: params.stepSourceLocations,
|
|
114
117
|
fillSite: stepFillSite,
|
|
@@ -125,7 +128,9 @@ function normalizeJob(params) {
|
|
|
125
128
|
issues: params.issues,
|
|
126
129
|
defaultRunnerLabels: params.context.defaultRunnerLabels
|
|
127
130
|
});
|
|
128
|
-
const checkout = normalizeJobCheckout(
|
|
131
|
+
const checkout = normalizeJobCheckout({
|
|
132
|
+
checkout: params.job.checkout
|
|
133
|
+
});
|
|
129
134
|
const jobEnv = normalizeEnv({
|
|
130
135
|
env: params.job.env,
|
|
131
136
|
path: [
|
|
@@ -147,7 +152,6 @@ function normalizeJob(params) {
|
|
|
147
152
|
const condition = normalizeIfCondition({
|
|
148
153
|
field: 'job.if',
|
|
149
154
|
source: params.job.if,
|
|
150
|
-
site: 'job-activation',
|
|
151
155
|
path: [
|
|
152
156
|
'jobs',
|
|
153
157
|
params.sourceName,
|
|
@@ -185,21 +189,37 @@ function normalizeJob(params) {
|
|
|
185
189
|
issues: params.issues,
|
|
186
190
|
allowedJobReferences
|
|
187
191
|
});
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
192
|
+
if (params.job.name !== undefined) {
|
|
193
|
+
validateLiteralName({
|
|
194
|
+
field: 'job.name',
|
|
195
|
+
dynamicField: 'execution_name',
|
|
196
|
+
source: params.job.name,
|
|
197
|
+
path: [
|
|
198
|
+
'jobs',
|
|
199
|
+
params.sourceName,
|
|
200
|
+
'name'
|
|
201
|
+
],
|
|
202
|
+
message: 'Job name must be literal. Move runtime interpolation to execution_name.',
|
|
203
|
+
issues: params.issues
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
const name = params.job.name === undefined ? undefined : unescapeLiteralName(params.job.name);
|
|
207
|
+
const executionName = params.job.execution_name === undefined ? undefined : parseInterpolationField({
|
|
208
|
+
field: 'job.execution_name',
|
|
209
|
+
source: params.job.execution_name,
|
|
191
210
|
path: [
|
|
192
211
|
'jobs',
|
|
193
212
|
params.sourceName,
|
|
194
|
-
'
|
|
213
|
+
'execution_name'
|
|
195
214
|
],
|
|
196
215
|
issues: params.issues,
|
|
216
|
+
fillSite: 'execution-creation',
|
|
197
217
|
allowedJobReferences,
|
|
198
218
|
typeOverlay: upstreamJobsTypeOverlay
|
|
199
219
|
}) ?? [
|
|
200
220
|
{
|
|
201
221
|
kind: 'literal',
|
|
202
|
-
value: params.job.
|
|
222
|
+
value: params.job.execution_name
|
|
203
223
|
}
|
|
204
224
|
];
|
|
205
225
|
return {
|
|
@@ -232,6 +252,9 @@ function normalizeJob(params) {
|
|
|
232
252
|
...name === undefined ? {} : {
|
|
233
253
|
name
|
|
234
254
|
},
|
|
255
|
+
...executionName === undefined ? {} : {
|
|
256
|
+
executionName
|
|
257
|
+
},
|
|
235
258
|
...jobEnv,
|
|
236
259
|
dependencies,
|
|
237
260
|
steps
|
|
@@ -326,53 +349,32 @@ function normalizeJobOutputs(params) {
|
|
|
326
349
|
}
|
|
327
350
|
];
|
|
328
351
|
templates[key] = template;
|
|
329
|
-
types[key] = inferJobOutputType(
|
|
330
|
-
sourceName: params.sourceName,
|
|
331
|
-
key,
|
|
332
|
-
source,
|
|
333
|
-
template,
|
|
334
|
-
issues: params.issues
|
|
335
|
-
});
|
|
352
|
+
types[key] = inferJobOutputType(template);
|
|
336
353
|
}
|
|
337
354
|
return {
|
|
338
355
|
templates,
|
|
339
356
|
types
|
|
340
357
|
};
|
|
341
358
|
}
|
|
342
|
-
function inferJobOutputType(
|
|
343
|
-
if (
|
|
344
|
-
const [segment] =
|
|
359
|
+
function inferJobOutputType(template) {
|
|
360
|
+
if (template.length !== 1) return 'string';
|
|
361
|
+
const [segment] = template;
|
|
345
362
|
if (segment?.kind !== 'deferred') return 'string';
|
|
346
363
|
const resultType = segment.expression.resultType;
|
|
347
364
|
if (resultType === undefined) return 'string';
|
|
348
|
-
|
|
349
|
-
params.issues.push(issue({
|
|
350
|
-
code: 'invalid-job-output',
|
|
351
|
-
message: `Job output "${params.key}" must resolve to a scalar value.`,
|
|
352
|
-
path: [
|
|
353
|
-
'jobs',
|
|
354
|
-
params.sourceName,
|
|
355
|
-
'outputs',
|
|
356
|
-
params.key
|
|
357
|
-
],
|
|
358
|
-
details: {
|
|
359
|
-
output: params.key,
|
|
360
|
-
source: params.source
|
|
361
|
-
}
|
|
362
|
-
}));
|
|
363
|
-
return 'string';
|
|
364
|
-
}
|
|
365
|
-
function isScalarExpressionType(type) {
|
|
366
|
-
return type === 'string' || type === 'int' || type === 'double' || type === 'bool' || type === 'null' || type === 'timestamp';
|
|
365
|
+
return resultType;
|
|
367
366
|
}
|
|
368
367
|
function normalizeJobSteps(params) {
|
|
369
368
|
const usedStepIds = new Map();
|
|
370
|
-
return params.job.steps.
|
|
369
|
+
return params.job.steps.flatMap((step, index)=>{
|
|
370
|
+
const normalized = normalizeStep({
|
|
371
371
|
step,
|
|
372
372
|
index,
|
|
373
373
|
sourceName: params.sourceName,
|
|
374
374
|
jobId: params.jobId,
|
|
375
375
|
allSteps: params.job.steps,
|
|
376
|
+
workflowEnvKeys: params.workflowEnvKeys,
|
|
377
|
+
jobEnvKeys: params.jobEnvKeys,
|
|
376
378
|
usedStepIds,
|
|
377
379
|
issues: params.issues,
|
|
378
380
|
stepSourceLocations: params.stepSourceLocations,
|
|
@@ -382,7 +384,11 @@ function normalizeJobSteps(params) {
|
|
|
382
384
|
upstreamJobs: params.upstreamJobs,
|
|
383
385
|
directNeedJobs: params.directNeedJobs,
|
|
384
386
|
context: params.context
|
|
385
|
-
})
|
|
387
|
+
});
|
|
388
|
+
return normalized === undefined ? [] : [
|
|
389
|
+
normalized
|
|
390
|
+
];
|
|
391
|
+
});
|
|
386
392
|
}
|
|
387
393
|
function normalizeStep(params) {
|
|
388
394
|
const stepKey = params.step.key;
|
|
@@ -442,7 +448,6 @@ function normalizeStep(params) {
|
|
|
442
448
|
const condition = normalizeIfCondition({
|
|
443
449
|
field: 'step.if',
|
|
444
450
|
source: params.step.if,
|
|
445
|
-
site: 'step-dispatch',
|
|
446
451
|
path: [
|
|
447
452
|
'jobs',
|
|
448
453
|
params.sourceName,
|
|
@@ -484,6 +489,21 @@ function normalizeStep(params) {
|
|
|
484
489
|
allowedJobReferences: params.allowedJobReferences,
|
|
485
490
|
typeOverlay
|
|
486
491
|
});
|
|
492
|
+
const workingDirectory = params.step.working_directory === undefined ? undefined : parseInterpolationField({
|
|
493
|
+
field: 'step.working_directory',
|
|
494
|
+
source: params.step.working_directory,
|
|
495
|
+
path: [
|
|
496
|
+
'jobs',
|
|
497
|
+
params.sourceName,
|
|
498
|
+
'steps',
|
|
499
|
+
params.index,
|
|
500
|
+
'working_directory'
|
|
501
|
+
],
|
|
502
|
+
issues: params.issues,
|
|
503
|
+
fillSite: params.fillSite,
|
|
504
|
+
allowedJobReferences: params.allowedJobReferences,
|
|
505
|
+
typeOverlay
|
|
506
|
+
});
|
|
487
507
|
const stepBase = {
|
|
488
508
|
id: stepId,
|
|
489
509
|
...stepKey === undefined ? {} : {
|
|
@@ -492,6 +512,9 @@ function normalizeStep(params) {
|
|
|
492
512
|
...params.step.name === undefined ? {} : {
|
|
493
513
|
name: params.step.name
|
|
494
514
|
},
|
|
515
|
+
...params.step.working_directory === undefined ? {} : {
|
|
516
|
+
workingDirectory: params.step.working_directory
|
|
517
|
+
},
|
|
495
518
|
...outputs === undefined ? {} : {
|
|
496
519
|
outputs
|
|
497
520
|
},
|
|
@@ -511,7 +534,10 @@ function normalizeStep(params) {
|
|
|
511
534
|
stepBase,
|
|
512
535
|
sourceName: params.sourceName,
|
|
513
536
|
stepIndex: params.index,
|
|
537
|
+
workflowEnvKeys: params.workflowEnvKeys,
|
|
538
|
+
jobEnvKeys: params.jobEnvKeys,
|
|
514
539
|
name,
|
|
540
|
+
workingDirectory,
|
|
515
541
|
issues: params.issues,
|
|
516
542
|
fillSite: params.fillSite,
|
|
517
543
|
allowedJobReferences: params.allowedJobReferences,
|
|
@@ -525,6 +551,7 @@ function normalizeStep(params) {
|
|
|
525
551
|
sourceName: params.sourceName,
|
|
526
552
|
stepIndex: params.index,
|
|
527
553
|
name,
|
|
554
|
+
workingDirectory,
|
|
528
555
|
issues: params.issues,
|
|
529
556
|
fillSite: params.fillSite,
|
|
530
557
|
allowedJobReferences: params.allowedJobReferences,
|
|
@@ -532,9 +559,51 @@ function normalizeStep(params) {
|
|
|
532
559
|
context: params.context
|
|
533
560
|
});
|
|
534
561
|
}
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
562
|
+
if (params.step.checkout !== undefined) {
|
|
563
|
+
return normalizeCheckoutStep({
|
|
564
|
+
step: params.step,
|
|
565
|
+
stepBase,
|
|
566
|
+
name,
|
|
567
|
+
sourceName: params.sourceName,
|
|
568
|
+
stepIndex: params.index,
|
|
569
|
+
issues: params.issues,
|
|
570
|
+
fillSite: params.fillSite,
|
|
571
|
+
allowedJobReferences: params.allowedJobReferences,
|
|
572
|
+
typeOverlay
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
// Keep the model-step union honest if callers bypass the document parser.
|
|
576
|
+
throw new Error(`Workflow step "${stepId}" is neither a run, agent, nor checkout step`);
|
|
577
|
+
}
|
|
578
|
+
function normalizeCheckoutStep(params) {
|
|
579
|
+
const checkout = params.step.checkout;
|
|
580
|
+
if (checkout === undefined) {
|
|
581
|
+
throw new Error('Checkout step normalization requires checkout settings');
|
|
582
|
+
}
|
|
583
|
+
const normalizedCheckout = normalizeCheckout({
|
|
584
|
+
checkout,
|
|
585
|
+
issues: params.issues,
|
|
586
|
+
path: [
|
|
587
|
+
'jobs',
|
|
588
|
+
params.sourceName,
|
|
589
|
+
'steps',
|
|
590
|
+
params.stepIndex,
|
|
591
|
+
'checkout'
|
|
592
|
+
],
|
|
593
|
+
fillSite: params.fillSite,
|
|
594
|
+
allowedJobReferences: params.allowedJobReferences,
|
|
595
|
+
typeOverlay: params.typeOverlay
|
|
596
|
+
});
|
|
597
|
+
return {
|
|
598
|
+
...params.stepBase,
|
|
599
|
+
kind: 'checkout',
|
|
600
|
+
checkout: normalizedCheckout,
|
|
601
|
+
...params.name === undefined ? {} : {
|
|
602
|
+
templates: {
|
|
603
|
+
name: params.name
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
};
|
|
538
607
|
}
|
|
539
608
|
function normalizeRunStep(params) {
|
|
540
609
|
if (params.step.run === undefined) {
|
|
@@ -569,9 +638,25 @@ function normalizeRunStep(params) {
|
|
|
569
638
|
allowedJobReferences: params.allowedJobReferences,
|
|
570
639
|
typeOverlay: params.typeOverlay
|
|
571
640
|
});
|
|
641
|
+
addReevaluatingCommandWarnings({
|
|
642
|
+
command: commandTemplate,
|
|
643
|
+
source: params.step.run,
|
|
644
|
+
workflowEnvKeys: params.workflowEnvKeys,
|
|
645
|
+
jobEnvKeys: params.jobEnvKeys,
|
|
646
|
+
stepEnvKeys: Object.keys(params.step.env ?? {}),
|
|
647
|
+
path: [
|
|
648
|
+
'jobs',
|
|
649
|
+
params.sourceName,
|
|
650
|
+
'steps',
|
|
651
|
+
params.stepIndex,
|
|
652
|
+
'run'
|
|
653
|
+
],
|
|
654
|
+
issues: params.issues
|
|
655
|
+
});
|
|
572
656
|
const templates = optionalRunStepTemplates({
|
|
573
657
|
command: commandTemplate,
|
|
574
658
|
name: params.name,
|
|
659
|
+
workingDirectory: params.workingDirectory,
|
|
575
660
|
env: stepEnv.templates?.env
|
|
576
661
|
});
|
|
577
662
|
return {
|
|
@@ -589,6 +674,90 @@ function normalizeRunStep(params) {
|
|
|
589
674
|
}
|
|
590
675
|
};
|
|
591
676
|
}
|
|
677
|
+
function addReevaluatingCommandWarnings(params) {
|
|
678
|
+
const envKeys = new Set([
|
|
679
|
+
...params.workflowEnvKeys,
|
|
680
|
+
...params.jobEnvKeys,
|
|
681
|
+
...params.stepEnvKeys
|
|
682
|
+
]);
|
|
683
|
+
let hoisted;
|
|
684
|
+
try {
|
|
685
|
+
hoisted = hoistPlannedRunCommand({
|
|
686
|
+
field: {
|
|
687
|
+
segments: params.command ?? [
|
|
688
|
+
{
|
|
689
|
+
kind: 'literal',
|
|
690
|
+
value: params.source
|
|
691
|
+
}
|
|
692
|
+
]
|
|
693
|
+
},
|
|
694
|
+
reservedNames: envKeys
|
|
695
|
+
});
|
|
696
|
+
} catch (error) {
|
|
697
|
+
if (!(error instanceof UnsafeRunInterpolationError)) return;
|
|
698
|
+
for (const occurrence of error.occurrences){
|
|
699
|
+
params.issues.push(issue({
|
|
700
|
+
code: 're-evaluating-command',
|
|
701
|
+
message: unsafeInterpolationWarningMessage(occurrence),
|
|
702
|
+
path: params.path,
|
|
703
|
+
severity: 'warning'
|
|
704
|
+
}));
|
|
705
|
+
}
|
|
706
|
+
if (error.partial === undefined) return;
|
|
707
|
+
hoisted = error.partial;
|
|
708
|
+
}
|
|
709
|
+
try {
|
|
710
|
+
const matches = classifyShellCodePosition({
|
|
711
|
+
command: hoisted.command,
|
|
712
|
+
workflowDataNames: [
|
|
713
|
+
...envKeys,
|
|
714
|
+
...hoisted.bindings.map((binding)=>binding.name)
|
|
715
|
+
]
|
|
716
|
+
}).matches;
|
|
717
|
+
for (const match of matches){
|
|
718
|
+
params.issues.push(issue({
|
|
719
|
+
code: 're-evaluating-command',
|
|
720
|
+
message: reevaluatingCommandWarningMessage(match.construct, match.name),
|
|
721
|
+
path: params.path,
|
|
722
|
+
severity: 'warning'
|
|
723
|
+
}));
|
|
724
|
+
}
|
|
725
|
+
} catch {
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
function unsafeInterpolationWarningMessage(error) {
|
|
730
|
+
const regionLabel = {
|
|
731
|
+
single: 'single-quoted shell text',
|
|
732
|
+
double: 'double-quoted shell text',
|
|
733
|
+
'dollar-single': 'ANSI-C shell text',
|
|
734
|
+
'dollar-double': 'dollar-double-quoted shell text',
|
|
735
|
+
'paren-sub': 'command substitution',
|
|
736
|
+
arith: 'shell arithmetic',
|
|
737
|
+
backtick: 'backtick substitution',
|
|
738
|
+
'param-brace': 'shell parameter expansion',
|
|
739
|
+
heredoc: 'a heredoc',
|
|
740
|
+
'line-comment': 'a shell comment',
|
|
741
|
+
escape: 'a shell escape'
|
|
742
|
+
}[error.region];
|
|
743
|
+
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.`;
|
|
744
|
+
}
|
|
745
|
+
function reevaluatingCommandWarningMessage(construct, name) {
|
|
746
|
+
const constructLabel = {
|
|
747
|
+
eval: 'eval',
|
|
748
|
+
'sh-c': 'sh -c',
|
|
749
|
+
'bash-c': 'bash -c',
|
|
750
|
+
source: 'source',
|
|
751
|
+
let: 'let',
|
|
752
|
+
'declare-i': 'declare -i',
|
|
753
|
+
arithmetic: 'shell arithmetic',
|
|
754
|
+
awk: 'awk',
|
|
755
|
+
jq: 'jq',
|
|
756
|
+
sed: 'sed',
|
|
757
|
+
'xargs-sh-c': 'xargs sh -c'
|
|
758
|
+
}[construct];
|
|
759
|
+
return `Value "$${name}" is passed to ${constructLabel} and re-executed as code. Hoisting cannot protect it; pass the value to a fixed program instead.`;
|
|
760
|
+
}
|
|
592
761
|
function normalizeAgentStep(params) {
|
|
593
762
|
if (params.step.prompt === undefined) {
|
|
594
763
|
throw new Error('Agent step normalization requires a prompt');
|
|
@@ -638,12 +807,28 @@ function normalizeAgentStep(params) {
|
|
|
638
807
|
allowedJobReferences: params.allowedJobReferences,
|
|
639
808
|
typeOverlay: params.typeOverlay
|
|
640
809
|
});
|
|
810
|
+
const thinkingTemplate = params.step.thinking === undefined ? undefined : parseInterpolationField({
|
|
811
|
+
field: 'agent.thinking',
|
|
812
|
+
source: params.step.thinking,
|
|
813
|
+
path: [
|
|
814
|
+
'jobs',
|
|
815
|
+
params.sourceName,
|
|
816
|
+
'steps',
|
|
817
|
+
params.stepIndex,
|
|
818
|
+
'thinking'
|
|
819
|
+
],
|
|
820
|
+
issues: params.issues,
|
|
821
|
+
fillSite: params.fillSite,
|
|
822
|
+
allowedJobReferences: params.allowedJobReferences,
|
|
823
|
+
typeOverlay: params.typeOverlay
|
|
824
|
+
});
|
|
641
825
|
validateAgentStep({
|
|
642
826
|
step: params.step,
|
|
643
827
|
sourceName: params.sourceName,
|
|
644
828
|
stepIndex: params.stepIndex,
|
|
645
829
|
issues: params.issues,
|
|
646
|
-
|
|
830
|
+
validateLiteralModel: params.step.model !== undefined && !hasInterpolationSyntax(params.step.model),
|
|
831
|
+
validateLiteralProvider: params.step.provider !== undefined && !hasInterpolationSyntax(params.step.provider),
|
|
647
832
|
agentValidationCatalog: params.context.agentValidationCatalog
|
|
648
833
|
});
|
|
649
834
|
const integrations = normalizeAgentIntegrations({
|
|
@@ -657,7 +842,9 @@ function normalizeAgentStep(params) {
|
|
|
657
842
|
prompt: promptTemplate,
|
|
658
843
|
model: modelTemplate,
|
|
659
844
|
provider: providerTemplate,
|
|
660
|
-
|
|
845
|
+
thinking: thinkingTemplate,
|
|
846
|
+
name: params.name,
|
|
847
|
+
workingDirectory: params.workingDirectory
|
|
661
848
|
});
|
|
662
849
|
return {
|
|
663
850
|
...params.stepBase,
|
|
@@ -689,46 +876,88 @@ function normalizeAgentStep(params) {
|
|
|
689
876
|
function validateAgentStep(params) {
|
|
690
877
|
validateHarnessThinking(params);
|
|
691
878
|
validateHarnessTools(params);
|
|
692
|
-
if (!params.validateLiteralProvider) return;
|
|
693
879
|
const providerId = params.step.provider;
|
|
694
|
-
if (providerId === undefined) return;
|
|
695
|
-
const provider = params.agentValidationCatalog.providers.find((entry)=>entry.id === providerId);
|
|
696
880
|
const harness = params.step.harness;
|
|
697
|
-
|
|
698
|
-
if (
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
881
|
+
const provider = providerId === undefined ? undefined : params.agentValidationCatalog.providers.find((entry)=>entry.id === providerId);
|
|
882
|
+
if (params.validateLiteralProvider && providerId !== undefined) {
|
|
883
|
+
if (provider === undefined) {
|
|
884
|
+
if (harness === undefined || harness === 'pi') return;
|
|
885
|
+
params.issues.push(issue({
|
|
886
|
+
code: 'invalid-provider',
|
|
887
|
+
message: `Provider "${providerId}" is not supported.`,
|
|
888
|
+
path: [
|
|
889
|
+
'jobs',
|
|
890
|
+
params.sourceName,
|
|
891
|
+
'steps',
|
|
892
|
+
params.stepIndex,
|
|
893
|
+
'provider'
|
|
894
|
+
],
|
|
895
|
+
details: {
|
|
896
|
+
provider: providerId
|
|
897
|
+
}
|
|
898
|
+
}));
|
|
899
|
+
return;
|
|
900
|
+
}
|
|
901
|
+
if (provider.support_status !== 'supported') {
|
|
902
|
+
params.issues.push(issue({
|
|
903
|
+
code: 'invalid-provider',
|
|
904
|
+
message: `Provider "${providerId}" is not supported.`,
|
|
905
|
+
path: [
|
|
906
|
+
'jobs',
|
|
907
|
+
params.sourceName,
|
|
908
|
+
'steps',
|
|
909
|
+
params.stepIndex,
|
|
910
|
+
'provider'
|
|
911
|
+
],
|
|
912
|
+
details: {
|
|
913
|
+
provider: providerId
|
|
914
|
+
}
|
|
915
|
+
}));
|
|
916
|
+
return;
|
|
917
|
+
}
|
|
918
|
+
if (harness === undefined) return;
|
|
919
|
+
const descriptor = params.agentValidationCatalog.harnesses.find((entry)=>entry.id === harness);
|
|
920
|
+
if (!descriptor?.supported_provider_ids.includes(providerId)) {
|
|
921
|
+
params.issues.push(issue({
|
|
922
|
+
code: 'harness-provider-incompatible',
|
|
923
|
+
message: `Harness "${harness}" does not support provider: ${providerId}. Supported providers: ${descriptor?.supported_provider_ids.join(', ') ?? ''}.`,
|
|
924
|
+
path: [
|
|
925
|
+
'jobs',
|
|
926
|
+
params.sourceName,
|
|
927
|
+
'steps',
|
|
928
|
+
params.stepIndex,
|
|
929
|
+
'provider'
|
|
930
|
+
],
|
|
931
|
+
details: {
|
|
932
|
+
harness,
|
|
933
|
+
provider: providerId,
|
|
934
|
+
supportedProviders: descriptor?.supported_provider_ids ?? []
|
|
935
|
+
}
|
|
936
|
+
}));
|
|
937
|
+
return;
|
|
938
|
+
}
|
|
714
939
|
}
|
|
715
|
-
if (harness === undefined) return;
|
|
940
|
+
if (!params.validateLiteralModel || providerId === undefined || harness === undefined) return;
|
|
941
|
+
if (provider === undefined || provider.support_status !== 'supported') return;
|
|
942
|
+
const model = params.step.model;
|
|
943
|
+
if (model === undefined) return;
|
|
716
944
|
const descriptor = params.agentValidationCatalog.harnesses.find((entry)=>entry.id === harness);
|
|
717
|
-
|
|
945
|
+
const modelIds = descriptor?.model_ids_by_provider?.[providerId];
|
|
946
|
+
if (modelIds === undefined || modelIds.includes(model)) return;
|
|
718
947
|
params.issues.push(issue({
|
|
719
|
-
code: '
|
|
720
|
-
message: `
|
|
948
|
+
code: 'invalid-model',
|
|
949
|
+
message: `Agent model "${model}" is not available for harness "${harness}" and provider "${providerId}".`,
|
|
721
950
|
path: [
|
|
722
951
|
'jobs',
|
|
723
952
|
params.sourceName,
|
|
724
953
|
'steps',
|
|
725
954
|
params.stepIndex,
|
|
726
|
-
'
|
|
955
|
+
'model'
|
|
727
956
|
],
|
|
728
957
|
details: {
|
|
729
958
|
harness,
|
|
730
959
|
provider: providerId,
|
|
731
|
-
|
|
960
|
+
model
|
|
732
961
|
}
|
|
733
962
|
}));
|
|
734
963
|
}
|
|
@@ -778,6 +1007,9 @@ function validateHarnessTools(params) {
|
|
|
778
1007
|
function validateHarnessThinking(params) {
|
|
779
1008
|
const { harness, thinking } = params.step;
|
|
780
1009
|
if (harness === undefined || thinking === undefined) return;
|
|
1010
|
+
// An interpolated level is only known when the step dispatches, so the agent
|
|
1011
|
+
// module checks it against the resolved harness there.
|
|
1012
|
+
if (hasInterpolationSyntax(thinking)) return;
|
|
781
1013
|
const supportedLevels = params.agentValidationCatalog.harnesses.find((entry)=>entry.id === harness)?.thinking_levels ?? [];
|
|
782
1014
|
if (supportedLevels.includes(thinking)) return;
|
|
783
1015
|
params.issues.push(issue({
|
|
@@ -895,7 +1127,7 @@ function validateRunnerLabels(params) {
|
|
|
895
1127
|
}
|
|
896
1128
|
}
|
|
897
1129
|
function optionalRunStepTemplates(params) {
|
|
898
|
-
if (params.command === undefined && params.name === undefined && params.env === undefined) {
|
|
1130
|
+
if (params.command === undefined && params.name === undefined && params.workingDirectory === undefined && params.env === undefined) {
|
|
899
1131
|
return undefined;
|
|
900
1132
|
}
|
|
901
1133
|
return {
|
|
@@ -905,13 +1137,16 @@ function optionalRunStepTemplates(params) {
|
|
|
905
1137
|
...params.name === undefined ? {} : {
|
|
906
1138
|
name: params.name
|
|
907
1139
|
},
|
|
1140
|
+
...params.workingDirectory === undefined ? {} : {
|
|
1141
|
+
workingDirectory: params.workingDirectory
|
|
1142
|
+
},
|
|
908
1143
|
...params.env === undefined ? {} : {
|
|
909
1144
|
env: params.env
|
|
910
1145
|
}
|
|
911
1146
|
};
|
|
912
1147
|
}
|
|
913
1148
|
function optionalAgentStepTemplates(params) {
|
|
914
|
-
if (params.prompt === undefined && params.model === undefined && params.provider === undefined && params.name === undefined) {
|
|
1149
|
+
if (params.prompt === undefined && params.model === undefined && params.provider === undefined && params.thinking === undefined && params.name === undefined && params.workingDirectory === undefined) {
|
|
915
1150
|
return undefined;
|
|
916
1151
|
}
|
|
917
1152
|
return {
|
|
@@ -924,8 +1159,14 @@ function optionalAgentStepTemplates(params) {
|
|
|
924
1159
|
...params.provider === undefined ? {} : {
|
|
925
1160
|
provider: params.provider
|
|
926
1161
|
},
|
|
1162
|
+
...params.thinking === undefined ? {} : {
|
|
1163
|
+
thinking: params.thinking
|
|
1164
|
+
},
|
|
927
1165
|
...params.name === undefined ? {} : {
|
|
928
1166
|
name: params.name
|
|
1167
|
+
},
|
|
1168
|
+
...params.workingDirectory === undefined ? {} : {
|
|
1169
|
+
workingDirectory: params.workingDirectory
|
|
929
1170
|
}
|
|
930
1171
|
};
|
|
931
1172
|
}
|