@shipfox/api-workflows 10.2.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 +8 -8
- package/CHANGELOG.md +117 -0
- package/README.md +75 -0
- package/dist/core/agent-defaults.d.ts +2 -1
- package/dist/core/agent-defaults.d.ts.map +1 -1
- package/dist/core/agent-defaults.js.map +1 -1
- package/dist/core/checkout.d.ts +7 -21
- package/dist/core/checkout.d.ts.map +1 -1
- package/dist/core/checkout.js +122 -41
- package/dist/core/checkout.js.map +1 -1
- package/dist/core/entities/job-execution.d.ts +8 -0
- package/dist/core/entities/job-execution.d.ts.map +1 -1
- package/dist/core/entities/job-execution.js +13 -1
- package/dist/core/entities/job-execution.js.map +1 -1
- package/dist/core/entities/job-listener-event.d.ts +2 -0
- package/dist/core/entities/job-listener-event.d.ts.map +1 -1
- package/dist/core/entities/job-listener-event.js.map +1 -1
- package/dist/core/entities/step.d.ts +13 -3
- package/dist/core/entities/step.d.ts.map +1 -1
- package/dist/core/entities/step.js +6 -0
- package/dist/core/entities/step.js.map +1 -1
- package/dist/core/entities/workflow-run-attempt.d.ts +1 -0
- package/dist/core/entities/workflow-run-attempt.d.ts.map +1 -1
- package/dist/core/entities/workflow-run-attempt.js.map +1 -1
- package/dist/core/entities/workflow-run.d.ts +17 -0
- package/dist/core/entities/workflow-run.d.ts.map +1 -1
- package/dist/core/entities/workflow-run.js.map +1 -1
- package/dist/core/errors.d.ts +9 -2
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +10 -5
- package/dist/core/errors.js.map +1 -1
- package/dist/core/job-execution.d.ts.map +1 -1
- package/dist/core/job-execution.js +14 -8
- package/dist/core/job-execution.js.map +1 -1
- package/dist/core/job-transition/decide-job-activation.d.ts +1 -0
- package/dist/core/job-transition/decide-job-activation.d.ts.map +1 -1
- package/dist/core/job-transition/decide-job-activation.js +1 -0
- package/dist/core/job-transition/decide-job-activation.js.map +1 -1
- package/dist/core/job-transition/derive-job-success.d.ts +1 -0
- package/dist/core/job-transition/derive-job-success.d.ts.map +1 -1
- package/dist/core/job-transition/derive-job-success.js +5 -8
- package/dist/core/job-transition/derive-job-success.js.map +1 -1
- package/dist/core/listener-execution-materialization.d.ts +6 -3
- package/dist/core/listener-execution-materialization.d.ts.map +1 -1
- package/dist/core/listener-execution-materialization.js +44 -6
- package/dist/core/listener-execution-materialization.js.map +1 -1
- package/dist/core/resolve-trigger-reference.d.ts +11 -0
- package/dist/core/resolve-trigger-reference.d.ts.map +1 -0
- package/dist/core/resolve-trigger-reference.js +39 -0
- package/dist/core/resolve-trigger-reference.js.map +1 -0
- package/dist/core/run-workflow.d.ts +1 -0
- package/dist/core/run-workflow.d.ts.map +1 -1
- package/dist/core/run-workflow.js +1 -0
- package/dist/core/run-workflow.js.map +1 -1
- package/dist/core/step-config/agent.d.ts +1 -2
- package/dist/core/step-config/agent.d.ts.map +1 -1
- package/dist/core/step-config/agent.js +22 -7
- package/dist/core/step-config/agent.js.map +1 -1
- package/dist/core/step-config/assemble-run-context.d.ts +19 -8
- package/dist/core/step-config/assemble-run-context.d.ts.map +1 -1
- package/dist/core/step-config/assemble-run-context.js +51 -19
- package/dist/core/step-config/assemble-run-context.js.map +1 -1
- package/dist/core/step-config/complete-step-dispatch-config.d.ts.map +1 -1
- package/dist/core/step-config/complete-step-dispatch-config.js +72 -4
- package/dist/core/step-config/complete-step-dispatch-config.js.map +1 -1
- package/dist/core/step-config/index.d.ts +1 -0
- package/dist/core/step-config/index.d.ts.map +1 -1
- package/dist/core/step-config/index.js +1 -0
- package/dist/core/step-config/index.js.map +1 -1
- package/dist/core/step-config/materialize-job-execution-steps.d.ts +1 -1
- package/dist/core/step-config/materialize-job-execution-steps.d.ts.map +1 -1
- package/dist/core/step-config/materialize-job-execution-steps.js +49 -8
- package/dist/core/step-config/materialize-job-execution-steps.js.map +1 -1
- package/dist/core/step-config/materialize-workflow-model.d.ts +3 -3
- package/dist/core/step-config/materialize-workflow-model.d.ts.map +1 -1
- package/dist/core/step-config/materialize-workflow-model.js +9 -5
- package/dist/core/step-config/materialize-workflow-model.js.map +1 -1
- package/dist/core/step-config/resolve-job-execution-name.d.ts +3 -3
- package/dist/core/step-config/resolve-job-execution-name.d.ts.map +1 -1
- package/dist/core/step-config/resolve-job-execution-name.js +24 -13
- package/dist/core/step-config/resolve-job-execution-name.js.map +1 -1
- package/dist/core/step-config/resolve-step-config.d.ts.map +1 -1
- package/dist/core/step-config/resolve-step-config.js +174 -9
- package/dist/core/step-config/resolve-step-config.js.map +1 -1
- package/dist/core/step-config/resolve-workflow-run-name.d.ts +19 -0
- package/dist/core/step-config/resolve-workflow-run-name.d.ts.map +1 -0
- package/dist/core/step-config/resolve-workflow-run-name.js +73 -0
- package/dist/core/step-config/resolve-workflow-run-name.js.map +1 -0
- package/dist/core/step-transition/decide-step-transition.js.map +1 -1
- package/dist/core/step-transition/evaluate-gate.d.ts +2 -1
- package/dist/core/step-transition/evaluate-gate.d.ts.map +1 -1
- package/dist/core/step-transition/evaluate-gate.js +5 -3
- package/dist/core/step-transition/evaluate-gate.js.map +1 -1
- package/dist/core/workflow-run-creation.d.ts +3 -0
- package/dist/core/workflow-run-creation.d.ts.map +1 -1
- package/dist/core/workflow-run-creation.js +22 -7
- package/dist/core/workflow-run-creation.js.map +1 -1
- package/dist/db/db.d.ts +278 -10
- package/dist/db/db.d.ts.map +1 -1
- package/dist/db/db.js +2 -0
- package/dist/db/db.js.map +1 -1
- package/dist/db/index.d.ts +3 -2
- 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/job-listener-events.d.ts +2 -0
- package/dist/db/job-listener-events.d.ts.map +1 -1
- package/dist/db/job-listener-events.js +1 -0
- package/dist/db/job-listener-events.js.map +1 -1
- package/dist/db/job-listeners.d.ts +2 -0
- package/dist/db/job-listeners.d.ts.map +1 -1
- package/dist/db/job-listeners.js +81 -21
- package/dist/db/job-listeners.js.map +1 -1
- package/dist/db/schema/job-executions.d.ts +3 -3
- package/dist/db/schema/job-executions.d.ts.map +1 -1
- package/dist/db/schema/job-executions.js +8 -4
- package/dist/db/schema/job-executions.js.map +1 -1
- package/dist/db/schema/job-listener-events.d.ts +20 -0
- package/dist/db/schema/job-listener-events.d.ts.map +1 -1
- package/dist/db/schema/job-listener-events.js +2 -0
- package/dist/db/schema/job-listener-events.js.map +1 -1
- package/dist/db/schema/step-attempts.js +2 -2
- package/dist/db/schema/step-attempts.js.map +1 -1
- package/dist/db/schema/steps.d.ts +4 -2
- package/dist/db/schema/steps.d.ts.map +1 -1
- package/dist/db/schema/steps.js +1 -1
- package/dist/db/schema/steps.js.map +1 -1
- package/dist/db/schema/workflow-run-attempts.d.ts +21 -2
- package/dist/db/schema/workflow-run-attempts.d.ts.map +1 -1
- package/dist/db/schema/workflow-run-attempts.js +2 -0
- package/dist/db/schema/workflow-run-attempts.js.map +1 -1
- package/dist/db/schema/workflow-run-counters.d.ts +43 -0
- package/dist/db/schema/workflow-run-counters.d.ts.map +1 -0
- package/dist/db/schema/workflow-run-counters.js +10 -0
- package/dist/db/schema/workflow-run-counters.js.map +1 -0
- package/dist/db/schema/workflow-runs.d.ts +54 -1
- package/dist/db/schema/workflow-runs.d.ts.map +1 -1
- package/dist/db/schema/workflow-runs.js +10 -2
- package/dist/db/schema/workflow-runs.js.map +1 -1
- package/dist/db/workflow-runs/job-executions.d.ts.map +1 -1
- package/dist/db/workflow-runs/job-executions.js +35 -14
- package/dist/db/workflow-runs/job-executions.js.map +1 -1
- package/dist/db/workflow-runs/jobs.d.ts +2 -0
- package/dist/db/workflow-runs/jobs.d.ts.map +1 -1
- package/dist/db/workflow-runs/jobs.js +9 -5
- package/dist/db/workflow-runs/jobs.js.map +1 -1
- package/dist/db/workflow-runs/queries.d.ts +51 -0
- package/dist/db/workflow-runs/queries.d.ts.map +1 -1
- package/dist/db/workflow-runs/queries.js +82 -2
- package/dist/db/workflow-runs/queries.js.map +1 -1
- package/dist/db/workflow-runs/run-create.d.ts +1 -0
- package/dist/db/workflow-runs/run-create.d.ts.map +1 -1
- package/dist/db/workflow-runs/run-create.js +159 -32
- package/dist/db/workflow-runs/run-create.js.map +1 -1
- package/dist/db/workflow-runs/run-rerun.d.ts.map +1 -1
- package/dist/db/workflow-runs/run-rerun.js +13 -5
- package/dist/db/workflow-runs/run-rerun.js.map +1 -1
- package/dist/db/workflow-runs/shared.d.ts +1 -0
- package/dist/db/workflow-runs/shared.d.ts.map +1 -1
- package/dist/db/workflow-runs/shared.js +2 -1
- package/dist/db/workflow-runs/shared.js.map +1 -1
- package/dist/db/workflow-runs/steps.js +3 -3
- package/dist/db/workflow-runs/steps.js.map +1 -1
- package/dist/db/workflow-runs.d.ts +4 -3
- package/dist/db/workflow-runs.d.ts.map +1 -1
- package/dist/db/workflow-runs.js +3 -2
- package/dist/db/workflow-runs.js.map +1 -1
- package/dist/metrics/instance.d.ts +1 -0
- package/dist/metrics/instance.d.ts.map +1 -1
- package/dist/metrics/instance.js +9 -0
- package/dist/metrics/instance.js.map +1 -1
- package/dist/presentation/dto/checkout-token.d.ts +1 -0
- package/dist/presentation/dto/checkout-token.d.ts.map +1 -1
- package/dist/presentation/dto/checkout-token.js +1 -0
- package/dist/presentation/dto/checkout-token.js.map +1 -1
- package/dist/presentation/dto/index.d.ts +1 -1
- package/dist/presentation/dto/index.d.ts.map +1 -1
- package/dist/presentation/dto/index.js +1 -1
- package/dist/presentation/dto/index.js.map +1 -1
- package/dist/presentation/dto/step.d.ts.map +1 -1
- package/dist/presentation/dto/step.js +2 -2
- package/dist/presentation/dto/step.js.map +1 -1
- package/dist/presentation/dto/workflow-run.d.ts +3 -1
- package/dist/presentation/dto/workflow-run.d.ts.map +1 -1
- package/dist/presentation/dto/workflow-run.js +34 -0
- package/dist/presentation/dto/workflow-run.js.map +1 -1
- package/dist/presentation/inter-module.d.ts.map +1 -1
- package/dist/presentation/inter-module.js +31 -3
- package/dist/presentation/inter-module.js.map +1 -1
- package/dist/presentation/routes/checkout-token.d.ts +1 -1
- package/dist/presentation/routes/checkout-token.d.ts.map +1 -1
- package/dist/presentation/routes/checkout-token.js +32 -20
- package/dist/presentation/routes/checkout-token.js.map +1 -1
- package/dist/presentation/routes/leased-step.d.ts +2 -0
- package/dist/presentation/routes/leased-step.d.ts.map +1 -1
- package/dist/presentation/routes/leased-step.js +2 -1
- package/dist/presentation/routes/leased-step.js.map +1 -1
- package/dist/presentation/routes/list-runs.d.ts.map +1 -1
- package/dist/presentation/routes/list-runs.js +9 -3
- package/dist/presentation/routes/list-runs.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/report-step.d.ts.map +1 -1
- package/dist/presentation/routes/report-step.js +9 -2
- package/dist/presentation/routes/report-step.js.map +1 -1
- package/dist/presentation/routes/require-accessible-run.d.ts.map +1 -1
- package/dist/presentation/routes/require-accessible-run.js +1 -1
- package/dist/presentation/routes/require-accessible-run.js.map +1 -1
- package/dist/presentation/subscribers/on-job-steps-settled.js +1 -1
- package/dist/presentation/subscribers/on-job-steps-settled.js.map +1 -1
- package/dist/presentation/subscribers/on-runner-job-claimed.d.ts.map +1 -1
- package/dist/presentation/subscribers/on-runner-job-claimed.js +21 -0
- package/dist/presentation/subscribers/on-runner-job-claimed.js.map +1 -1
- package/dist/temporal/activities/index.d.ts.map +1 -1
- package/dist/temporal/activities/index.js +2 -1
- package/dist/temporal/activities/index.js.map +1 -1
- package/dist/temporal/activities/orchestration-activities.d.ts +1 -0
- package/dist/temporal/activities/orchestration-activities.d.ts.map +1 -1
- package/dist/temporal/activities/orchestration-activities.js.map +1 -1
- package/dist/temporal/constants.d.ts +1 -0
- package/dist/temporal/constants.d.ts.map +1 -1
- package/dist/temporal/constants.js +1 -0
- package/dist/temporal/constants.js.map +1 -1
- package/dist/temporal/index.d.ts +1 -1
- package/dist/temporal/index.d.ts.map +1 -1
- package/dist/temporal/index.js +1 -1
- package/dist/temporal/index.js.map +1 -1
- package/dist/temporal/workflows/index.bundle.js +82 -43
- package/dist/temporal/workflows/index.d.ts +1 -1
- package/dist/temporal/workflows/index.d.ts.map +1 -1
- package/dist/temporal/workflows/index.js +1 -1
- package/dist/temporal/workflows/index.js.map +1 -1
- package/dist/temporal/workflows/job-execution-orchestration.d.ts +5 -0
- package/dist/temporal/workflows/job-execution-orchestration.d.ts.map +1 -1
- package/dist/temporal/workflows/job-execution-orchestration.js +77 -43
- package/dist/temporal/workflows/job-execution-orchestration.js.map +1 -1
- package/dist/temporal/workflows/test-env.d.ts +5 -1
- package/dist/temporal/workflows/test-env.d.ts.map +1 -1
- package/dist/temporal/workflows/test-env.js +32 -11
- package/dist/temporal/workflows/test-env.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/drizzle/0000_initial.sql +14 -2
- package/drizzle/0001_windy_sunfire.sql +1 -0
- package/drizzle/meta/0000_snapshot.json +93 -1
- package/drizzle/meta/0001_snapshot.json +1711 -0
- package/drizzle/meta/_journal.json +2 -2
- package/package.json +21 -21
- package/src/core/agent-defaults.ts +2 -1
- package/src/core/checkout.test.ts +378 -67
- package/src/core/checkout.ts +142 -59
- package/src/core/entities/job-execution.test.ts +21 -0
- package/src/core/entities/job-execution.ts +24 -0
- package/src/core/entities/job-listener-event.ts +3 -0
- package/src/core/entities/step.ts +15 -3
- package/src/core/entities/workflow-run-attempt.ts +1 -0
- package/src/core/entities/workflow-run.ts +16 -0
- package/src/core/errors.ts +21 -7
- package/src/core/job-execution.ts +27 -4
- package/src/core/job-transition/decide-job-activation.test.ts +4 -0
- package/src/core/job-transition/decide-job-activation.ts +2 -0
- package/src/core/job-transition/derive-job-success.test.ts +12 -0
- package/src/core/job-transition/derive-job-success.ts +6 -9
- package/src/core/listener-execution-materialization.test.ts +2 -0
- package/src/core/listener-execution-materialization.ts +57 -11
- package/src/core/resolve-trigger-reference.test.ts +160 -0
- package/src/core/resolve-trigger-reference.ts +53 -0
- package/src/core/run-workflow.ts +2 -0
- package/src/core/step-config/agent.ts +29 -12
- package/src/core/step-config/assemble-run-context.test.ts +149 -6
- package/src/core/step-config/assemble-run-context.ts +87 -25
- package/src/core/step-config/complete-step-dispatch-config.test.ts +125 -2
- package/src/core/step-config/complete-step-dispatch-config.ts +83 -1
- package/src/core/step-config/index.ts +7 -0
- package/src/core/step-config/materialize-job-execution-steps.test.ts +250 -1
- package/src/core/step-config/materialize-job-execution-steps.ts +57 -10
- package/src/core/step-config/materialize-workflow-model.test.ts +212 -4
- package/src/core/step-config/materialize-workflow-model.ts +33 -28
- package/src/core/step-config/resolve-job-execution-name.test.ts +37 -15
- package/src/core/step-config/resolve-job-execution-name.ts +30 -15
- package/src/core/step-config/resolve-step-config.ts +175 -10
- package/src/core/step-config/resolve-workflow-run-name.test.ts +115 -0
- package/src/core/step-config/resolve-workflow-run-name.ts +108 -0
- package/src/core/step-transition/decide-step-transition.ts +2 -2
- package/src/core/step-transition/evaluate-gate.test.ts +8 -0
- package/src/core/step-transition/evaluate-gate.ts +8 -1
- package/src/core/workflow-run-creation.test.ts +7 -4
- package/src/core/workflow-run-creation.ts +13 -7
- package/src/db/db.ts +2 -0
- package/src/db/index.ts +6 -0
- package/src/db/job-listener-events.test.ts +19 -0
- package/src/db/job-listener-events.ts +3 -0
- package/src/db/job-listeners.test.ts +187 -5
- package/src/db/job-listeners.ts +122 -30
- package/src/db/schema/job-executions.ts +12 -5
- package/src/db/schema/job-listener-events.ts +3 -0
- package/src/db/schema/step-attempts.ts +2 -2
- package/src/db/schema/steps.ts +2 -1
- package/src/db/schema/workflow-run-attempts.ts +2 -0
- package/src/db/schema/workflow-run-counters.ts +13 -0
- package/src/db/schema/workflow-runs.ts +11 -2
- package/src/db/workflow-runs/job-executions.ts +37 -15
- package/src/db/workflow-runs/jobs.ts +17 -4
- package/src/db/workflow-runs/queries.test.ts +206 -0
- package/src/db/workflow-runs/queries.ts +146 -2
- package/src/db/workflow-runs/run-create.test.ts +606 -8
- package/src/db/workflow-runs/run-create.ts +185 -40
- package/src/db/workflow-runs/run-rerun.test.ts +61 -2
- package/src/db/workflow-runs/run-rerun.ts +15 -5
- package/src/db/workflow-runs/shared.ts +2 -0
- package/src/db/workflow-runs/steps.ts +3 -3
- package/src/db/workflow-runs.ts +7 -0
- package/src/metrics/instance.ts +14 -0
- package/src/presentation/dto/checkout-token.test.ts +19 -9
- package/src/presentation/dto/checkout-token.ts +2 -1
- package/src/presentation/dto/index.ts +1 -1
- package/src/presentation/dto/job.test.ts +9 -0
- package/src/presentation/dto/step.test.ts +12 -0
- package/src/presentation/dto/step.ts +5 -2
- package/src/presentation/dto/workflow-run.ts +44 -2
- package/src/presentation/inter-module.test.ts +165 -0
- package/src/presentation/inter-module.ts +38 -4
- package/src/presentation/routes/cancel-run.test.ts +1 -1
- package/src/presentation/routes/checkout-token.test.ts +357 -142
- package/src/presentation/routes/checkout-token.ts +56 -26
- package/src/presentation/routes/get-run-aggregates.test.ts +1 -1
- package/src/presentation/routes/get-run.test.ts +29 -1
- package/src/presentation/routes/get-step-secrets.test.ts +7 -2
- package/src/presentation/routes/leased-step.ts +9 -1
- package/src/presentation/routes/list-run-attempts.test.ts +1 -1
- package/src/presentation/routes/list-runs.test.ts +58 -1
- package/src/presentation/routes/list-runs.ts +8 -3
- package/src/presentation/routes/report-step.test.ts +25 -0
- package/src/presentation/routes/report-step.ts +10 -2
- package/src/presentation/routes/require-accessible-run.ts +4 -1
- package/src/presentation/routes/rerun-run.test.ts +1 -1
- package/src/presentation/subscribers/on-job-steps-settled.ts +1 -1
- package/src/presentation/subscribers/on-runner-job-claimed.test.ts +49 -0
- package/src/presentation/subscribers/on-runner-job-claimed.ts +22 -0
- package/src/temporal/activities/index.ts +1 -0
- package/src/temporal/activities/orchestration-activities.ts +1 -0
- package/src/temporal/constants.ts +1 -0
- package/src/temporal/index.ts +1 -0
- package/src/temporal/workflows/index.ts +1 -0
- package/src/temporal/workflows/job-execution-orchestration.test.ts +142 -6
- package/src/temporal/workflows/job-execution-orchestration.ts +99 -50
- package/src/temporal/workflows/run-orchestration.test.ts +1 -1
- package/src/temporal/workflows/test-env.ts +50 -13
- package/test/factories/project.ts +2 -0
- package/test/factories/workflow-model.ts +80 -26
- package/test/factories/workflow-run.ts +4 -1
- package/test/fixtures/agent-inter-module.ts +2 -1
- package/test/fixtures/job-with-steps.ts +1 -1
- package/tsconfig.build.tsbuildinfo +1 -1
- package/drizzle/0001_agent_tool_materialization.sql +0 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type {WorkflowEnvTemplates, WorkflowModel} from '@shipfox/api-definitions-dto';
|
|
2
|
+
import {WORKFLOW_MODEL_CHECKOUT_TARGET_FIELDS} from '@shipfox/api-definitions-dto';
|
|
2
3
|
import {
|
|
3
4
|
type AvailabilitySite,
|
|
4
5
|
capTraceEntries,
|
|
@@ -14,6 +15,7 @@ import type {StepConfigDispatchPlan} from '#core/entities/step.js';
|
|
|
14
15
|
import {resolveAgentStepConfig} from './agent.js';
|
|
15
16
|
import {
|
|
16
17
|
freezeStepField,
|
|
18
|
+
resolveStepField,
|
|
17
19
|
type StepConfigField,
|
|
18
20
|
type WorkflowStepEvaluationTraceEntry,
|
|
19
21
|
type WorkflowStepTemplateDiagnostic,
|
|
@@ -25,6 +27,7 @@ type WorkflowModelJob = WorkflowModel['jobs'][number];
|
|
|
25
27
|
type WorkflowModelStep = WorkflowModelJob['steps'][number];
|
|
26
28
|
type WorkflowModelRunStep = Extract<WorkflowModelStep, {kind: 'run'}>;
|
|
27
29
|
type WorkflowModelAgentStep = Extract<WorkflowModelStep, {kind: 'agent'}>;
|
|
30
|
+
type WorkflowModelCheckoutStep = Extract<WorkflowModelStep, {kind: 'checkout'}>;
|
|
28
31
|
|
|
29
32
|
export type {StepConfigField, WorkflowStepTemplateDiagnostic};
|
|
30
33
|
|
|
@@ -62,6 +65,22 @@ interface BuiltStepConfig {
|
|
|
62
65
|
readonly hasTemplates: boolean;
|
|
63
66
|
}
|
|
64
67
|
|
|
68
|
+
interface WorkingDirectoryConfig {
|
|
69
|
+
readonly config: Record<string, unknown>;
|
|
70
|
+
readonly configPlan: Pick<StepConfigDispatchPlan, 'working_directory'> | undefined;
|
|
71
|
+
readonly diagnostics: readonly WorkflowStepTemplateDiagnostic[];
|
|
72
|
+
readonly trace: readonly WorkflowStepEvaluationTraceEntry[];
|
|
73
|
+
readonly hasTemplates: boolean;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
interface CheckoutConfig {
|
|
77
|
+
readonly config: Record<string, unknown>;
|
|
78
|
+
readonly configPlan: Pick<StepConfigDispatchPlan, 'checkout'> | undefined;
|
|
79
|
+
readonly diagnostics: readonly WorkflowStepTemplateDiagnostic[];
|
|
80
|
+
readonly trace: readonly WorkflowStepEvaluationTraceEntry[];
|
|
81
|
+
readonly hasTemplates: boolean;
|
|
82
|
+
}
|
|
83
|
+
|
|
65
84
|
export async function resolveStepConfig(
|
|
66
85
|
params: ResolveStepConfigParams,
|
|
67
86
|
): Promise<ResolvedStepConfig> {
|
|
@@ -87,17 +106,34 @@ async function buildStepConfig(
|
|
|
87
106
|
): Promise<BuiltStepConfig> {
|
|
88
107
|
const gate = gateConfigForStep(params.step);
|
|
89
108
|
const outputs = outputsConfigForStep(params.step);
|
|
109
|
+
const workingDirectory = resolveWorkingDirectoryConfig(params);
|
|
90
110
|
const runStep = runStepOrNull(params.step);
|
|
91
111
|
const isRunStep = runStep !== null;
|
|
92
112
|
|
|
93
113
|
if (isRunStep) {
|
|
94
114
|
const run = resolveRunStepConfig({...params, step: runStep});
|
|
95
115
|
return {
|
|
96
|
-
config: {...run.config, ...gate, ...outputs},
|
|
97
|
-
configPlan: run.configPlan,
|
|
98
|
-
diagnostics: run.diagnostics,
|
|
99
|
-
trace: run.trace,
|
|
100
|
-
hasTemplates: run.hasTemplates,
|
|
116
|
+
config: {...workingDirectory.config, ...run.config, ...gate, ...outputs},
|
|
117
|
+
configPlan: mergeConfigPlans(run.configPlan, workingDirectory.configPlan),
|
|
118
|
+
diagnostics: [...workingDirectory.diagnostics, ...run.diagnostics],
|
|
119
|
+
trace: [...workingDirectory.trace, ...run.trace],
|
|
120
|
+
hasTemplates: run.hasTemplates || workingDirectory.hasTemplates,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const checkoutStep = checkoutStepOrNull(params.step);
|
|
125
|
+
if (checkoutStep !== null) {
|
|
126
|
+
const checkout = resolveCheckoutStepConfig({...params, step: checkoutStep});
|
|
127
|
+
return {
|
|
128
|
+
config: {
|
|
129
|
+
...checkout.config,
|
|
130
|
+
...gate,
|
|
131
|
+
...outputs,
|
|
132
|
+
},
|
|
133
|
+
configPlan: checkout.configPlan === undefined ? null : checkout.configPlan,
|
|
134
|
+
diagnostics: checkout.diagnostics,
|
|
135
|
+
trace: checkout.trace,
|
|
136
|
+
hasTemplates: checkout.hasTemplates,
|
|
101
137
|
};
|
|
102
138
|
}
|
|
103
139
|
|
|
@@ -106,14 +142,81 @@ async function buildStepConfig(
|
|
|
106
142
|
|
|
107
143
|
const agent = await resolveAgentStepConfig({...params, step: agentStep});
|
|
108
144
|
return {
|
|
109
|
-
config: {...agent.config, ...gate, ...outputs},
|
|
110
|
-
configPlan: agent.configPlan,
|
|
111
|
-
diagnostics: agent.diagnostics,
|
|
112
|
-
trace: agent.trace,
|
|
113
|
-
hasTemplates: agent.hasTemplates,
|
|
145
|
+
config: {...workingDirectory.config, ...agent.config, ...gate, ...outputs},
|
|
146
|
+
configPlan: mergeConfigPlans(agent.configPlan, workingDirectory.configPlan),
|
|
147
|
+
diagnostics: [...workingDirectory.diagnostics, ...agent.diagnostics],
|
|
148
|
+
trace: [...workingDirectory.trace, ...agent.trace],
|
|
149
|
+
hasTemplates: agent.hasTemplates || workingDirectory.hasTemplates,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function resolveWorkingDirectoryConfig(
|
|
154
|
+
params: Omit<BuildStepConfigParams, 'context'> & {readonly context: WorkflowEvaluationContext},
|
|
155
|
+
): WorkingDirectoryConfig {
|
|
156
|
+
const value = params.step.workingDirectory;
|
|
157
|
+
const template = params.step.templates?.workingDirectory;
|
|
158
|
+
if (value === undefined) {
|
|
159
|
+
return {
|
|
160
|
+
config: {},
|
|
161
|
+
configPlan: undefined,
|
|
162
|
+
diagnostics: [],
|
|
163
|
+
trace: [],
|
|
164
|
+
hasTemplates: template !== undefined,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (template === undefined || params.mode === 'authored') {
|
|
169
|
+
return {
|
|
170
|
+
config: {working_directory: value},
|
|
171
|
+
configPlan: undefined,
|
|
172
|
+
diagnostics: [],
|
|
173
|
+
trace: [],
|
|
174
|
+
hasTemplates: template !== undefined,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const resolved = resolveStepField({
|
|
179
|
+
field: 'step.working_directory',
|
|
180
|
+
template: {segments: template},
|
|
181
|
+
context: params.context,
|
|
182
|
+
definitionId: params.definitionId,
|
|
183
|
+
errorField: 'step.working_directory',
|
|
184
|
+
});
|
|
185
|
+
const trace = resolved.trace.map((entry) => ({
|
|
186
|
+
...entry,
|
|
187
|
+
field: 'step.working_directory' as const,
|
|
188
|
+
}));
|
|
189
|
+
const diagnostics = resolved.diagnostics.map((diagnostic) => ({
|
|
190
|
+
...diagnostic,
|
|
191
|
+
field: 'step.working_directory' as const,
|
|
192
|
+
}));
|
|
193
|
+
if (resolved.kind === 'residual') {
|
|
194
|
+
return {
|
|
195
|
+
config: {},
|
|
196
|
+
configPlan: {working_directory: resolved.field},
|
|
197
|
+
diagnostics,
|
|
198
|
+
trace,
|
|
199
|
+
hasTemplates: true,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
config: {working_directory: resolved.value},
|
|
205
|
+
configPlan: undefined,
|
|
206
|
+
diagnostics,
|
|
207
|
+
trace,
|
|
208
|
+
hasTemplates: true,
|
|
114
209
|
};
|
|
115
210
|
}
|
|
116
211
|
|
|
212
|
+
function mergeConfigPlans(
|
|
213
|
+
primary: StepConfigDispatchPlan | null,
|
|
214
|
+
secondary: Pick<StepConfigDispatchPlan, 'working_directory'> | undefined,
|
|
215
|
+
): StepConfigDispatchPlan | null {
|
|
216
|
+
if (secondary === undefined) return primary;
|
|
217
|
+
return {...(primary ?? {}), ...secondary};
|
|
218
|
+
}
|
|
219
|
+
|
|
117
220
|
function evaluationContext(params: {
|
|
118
221
|
readonly context: WorkflowExpressionEvaluationContext;
|
|
119
222
|
readonly site: AvailabilitySite;
|
|
@@ -131,6 +234,68 @@ function agentStepOrNull(step: WorkflowModelStep): WorkflowModelAgentStep | null
|
|
|
131
234
|
return isAgentStep ? step : null;
|
|
132
235
|
}
|
|
133
236
|
|
|
237
|
+
function checkoutStepOrNull(step: WorkflowModelStep): WorkflowModelCheckoutStep | null {
|
|
238
|
+
const isCheckoutStep = step.kind === 'checkout';
|
|
239
|
+
return isCheckoutStep ? step : null;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function resolveCheckoutStepConfig(
|
|
243
|
+
params: Omit<BuildStepConfigParams, 'context' | 'step'> & {
|
|
244
|
+
readonly context: WorkflowEvaluationContext;
|
|
245
|
+
readonly step: WorkflowModelCheckoutStep;
|
|
246
|
+
},
|
|
247
|
+
): CheckoutConfig {
|
|
248
|
+
const checkout = params.step.checkout;
|
|
249
|
+
const config: Record<string, unknown> = {
|
|
250
|
+
fetch_depth: checkout.fetchDepth,
|
|
251
|
+
permissions: checkout.permissions,
|
|
252
|
+
persist_credentials: checkout.persistCredentials,
|
|
253
|
+
...(checkout.force === undefined ? {} : {force: checkout.force}),
|
|
254
|
+
};
|
|
255
|
+
const checkoutConfig: Record<string, unknown> = config;
|
|
256
|
+
const checkoutPlan: NonNullable<StepConfigDispatchPlan['checkout']> = {};
|
|
257
|
+
const diagnostics: WorkflowStepTemplateDiagnostic[] = [];
|
|
258
|
+
const trace: WorkflowStepEvaluationTraceEntry[] = [];
|
|
259
|
+
let hasTemplates = false;
|
|
260
|
+
|
|
261
|
+
for (const [key, field] of WORKFLOW_MODEL_CHECKOUT_TARGET_FIELDS) {
|
|
262
|
+
const template = checkout.templates?.[key];
|
|
263
|
+
if (template !== undefined) hasTemplates = true;
|
|
264
|
+
|
|
265
|
+
const value = checkout[key];
|
|
266
|
+
if (value === undefined) continue;
|
|
267
|
+
|
|
268
|
+
if (template === undefined || params.mode === 'authored') {
|
|
269
|
+
checkoutConfig[key] = value;
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const resolved = resolveStepField({
|
|
274
|
+
field,
|
|
275
|
+
template: {segments: template},
|
|
276
|
+
context: params.context,
|
|
277
|
+
definitionId: params.definitionId,
|
|
278
|
+
errorField: field,
|
|
279
|
+
});
|
|
280
|
+
trace.push(...resolved.trace.map((entry) => ({...entry, field})));
|
|
281
|
+
diagnostics.push(...resolved.diagnostics.map((diagnostic) => ({...diagnostic, field})));
|
|
282
|
+
|
|
283
|
+
if (resolved.kind === 'residual') {
|
|
284
|
+
checkoutPlan[key] = resolved.field;
|
|
285
|
+
} else {
|
|
286
|
+
checkoutConfig[key] = resolved.value;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return {
|
|
291
|
+
config: {checkout: config},
|
|
292
|
+
configPlan: Object.keys(checkoutPlan).length === 0 ? undefined : {checkout: checkoutPlan},
|
|
293
|
+
diagnostics,
|
|
294
|
+
trace,
|
|
295
|
+
hasTemplates,
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
|
|
134
299
|
function gateConfigForStep(step: WorkflowModelStep): Record<string, unknown> {
|
|
135
300
|
const hasGate = step.gate !== undefined;
|
|
136
301
|
return hasGate ? {gate: stepGateConfig(step.gate)} : {};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import {workflowModel} from '#test/index.js';
|
|
2
|
+
import {resolveWorkflowRunName, sanitizeWorkflowDisplayText} from './resolve-workflow-run-name.js';
|
|
3
|
+
|
|
4
|
+
function template(source: string): string {
|
|
5
|
+
return `\${{ ${source} }}`;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
describe('resolveWorkflowRunName', () => {
|
|
9
|
+
it('leaves an omitted run name unset', () => {
|
|
10
|
+
expect(
|
|
11
|
+
resolveWorkflowRunName({
|
|
12
|
+
runName: undefined,
|
|
13
|
+
context: {},
|
|
14
|
+
}),
|
|
15
|
+
).toEqual({value: null, trace: []});
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('resolves a run name from trigger-event context', () => {
|
|
19
|
+
const model = workflowModel({runName: `Deploy ${template('event.environment')}`});
|
|
20
|
+
|
|
21
|
+
const result = resolveWorkflowRunName({
|
|
22
|
+
runName: model.runName,
|
|
23
|
+
context: {event: {environment: 'production'}},
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
expect(result).toMatchObject({
|
|
27
|
+
value: 'Deploy production',
|
|
28
|
+
trace: [
|
|
29
|
+
{
|
|
30
|
+
field: 'workflow.run_name',
|
|
31
|
+
expression: 'event.environment',
|
|
32
|
+
evaluatedAt: 'run-creation',
|
|
33
|
+
value: 'production',
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
});
|
|
37
|
+
expect(result.degradation).toBeUndefined();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('loads and resolves input values through the same creation context', () => {
|
|
41
|
+
const model = workflowModel({runName: `Deploy ${template('inputs.environment')}`});
|
|
42
|
+
|
|
43
|
+
const result = resolveWorkflowRunName({
|
|
44
|
+
runName: model.runName,
|
|
45
|
+
context: {inputs: {environment: 'staging'}},
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
expect(result.value).toBe('Deploy staging');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it.each([
|
|
52
|
+
[{event: {}}, 'missing_value'],
|
|
53
|
+
[{event: {environment: ''}}, 'empty_value'],
|
|
54
|
+
] as const)('falls back without throwing for %s', (context, cause) => {
|
|
55
|
+
const model = workflowModel({runName: template('event.environment')});
|
|
56
|
+
|
|
57
|
+
const result = resolveWorkflowRunName({
|
|
58
|
+
runName: model.runName,
|
|
59
|
+
context,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
expect(result).toMatchObject({value: null, degradation: {cause}});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('sanitizes control and format characters without failing the run', () => {
|
|
66
|
+
const model = workflowModel({runName: template('event.environment')});
|
|
67
|
+
|
|
68
|
+
const result = resolveWorkflowRunName({
|
|
69
|
+
runName: model.runName,
|
|
70
|
+
context: {event: {environment: 'prod\u202e\nblue'}},
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
expect(result).toMatchObject({
|
|
74
|
+
value: 'prod blue',
|
|
75
|
+
degradation: {cause: 'sanitization'},
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('degrades when expression evaluation throws', () => {
|
|
80
|
+
const model = workflowModel({runName: template('1 / 0')});
|
|
81
|
+
|
|
82
|
+
const result = resolveWorkflowRunName({
|
|
83
|
+
runName: model.runName,
|
|
84
|
+
context: {},
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
expect(result).toMatchObject({
|
|
88
|
+
value: null,
|
|
89
|
+
degradation: {cause: 'evaluation_error', expression: '1 / 0'},
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('degrades when sanitization removes the complete resolved value', () => {
|
|
94
|
+
const model = workflowModel({runName: template('event.environment')});
|
|
95
|
+
|
|
96
|
+
const result = resolveWorkflowRunName({
|
|
97
|
+
runName: model.runName,
|
|
98
|
+
context: {event: {environment: '\u200b'}},
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
expect(result).toMatchObject({value: null, degradation: {cause: 'sanitization'}});
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe('sanitizeWorkflowDisplayText', () => {
|
|
106
|
+
it('trims and bounds display text', () => {
|
|
107
|
+
expect(sanitizeWorkflowDisplayText(` ${'a'.repeat(300)} `)).toBe('a'.repeat(255));
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('bounds by Unicode code point without splitting an emoji', () => {
|
|
111
|
+
const value = `${'a'.repeat(254)}😀`;
|
|
112
|
+
|
|
113
|
+
expect(sanitizeWorkflowDisplayText(value)).toBe(value);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import type {WorkflowModel} from '@shipfox/api-definitions-dto';
|
|
2
|
+
import {
|
|
3
|
+
capTraceEntries,
|
|
4
|
+
getWorkflowInterpolationFieldFailurePolicy,
|
|
5
|
+
type ResolvedFieldSegment,
|
|
6
|
+
resolveFieldAtSite,
|
|
7
|
+
type WorkflowExpressionEvaluationContext,
|
|
8
|
+
WorkflowTemplateResolutionError,
|
|
9
|
+
} from '@shipfox/expression';
|
|
10
|
+
import type {PersistedEvaluationTraceEntry} from '#core/entities/step.js';
|
|
11
|
+
|
|
12
|
+
const DISALLOWED_DISPLAY_CHARACTER_RE = /[\p{Cc}\p{Cf}]/gu;
|
|
13
|
+
const MAX_WORKFLOW_RUN_NAME_LENGTH = 255;
|
|
14
|
+
|
|
15
|
+
export type WorkflowRunNameResolutionCause =
|
|
16
|
+
| 'missing_value'
|
|
17
|
+
| 'evaluation_error'
|
|
18
|
+
| 'empty_value'
|
|
19
|
+
| 'sanitization';
|
|
20
|
+
|
|
21
|
+
export interface WorkflowRunNameDegradation {
|
|
22
|
+
readonly cause: WorkflowRunNameResolutionCause;
|
|
23
|
+
readonly expression?: string | undefined;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface ResolveWorkflowRunNameResult {
|
|
27
|
+
readonly value: string | null;
|
|
28
|
+
readonly trace: readonly PersistedEvaluationTraceEntry[];
|
|
29
|
+
readonly degradation?: WorkflowRunNameDegradation | undefined;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function resolveWorkflowRunName(params: {
|
|
33
|
+
readonly runName: WorkflowModel['runName'];
|
|
34
|
+
readonly context: WorkflowExpressionEvaluationContext;
|
|
35
|
+
}): ResolveWorkflowRunNameResult {
|
|
36
|
+
if (params.runName === undefined) return {value: null, trace: []};
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const resolved = resolveFieldAtSite({
|
|
40
|
+
field: {segments: params.runName},
|
|
41
|
+
site: 'run-creation',
|
|
42
|
+
context: params.context,
|
|
43
|
+
failurePolicy: getWorkflowInterpolationFieldFailurePolicy('workflow.run_name'),
|
|
44
|
+
});
|
|
45
|
+
const trace = capTraceEntries(
|
|
46
|
+
resolved.trace.map((entry) => ({...entry, field: 'workflow.run_name'})),
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
if (resolved.kind !== 'frozen' || resolved.diagnostics.length > 0) {
|
|
50
|
+
return degraded(trace, {
|
|
51
|
+
cause: 'missing_value',
|
|
52
|
+
expression: resolved.diagnostics[0]?.expression ?? firstExpressionSource(params.runName),
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (resolved.value === '') {
|
|
57
|
+
return degraded(trace, {
|
|
58
|
+
cause: 'empty_value',
|
|
59
|
+
expression: firstExpressionSource(params.runName),
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const sanitized = sanitizeWorkflowDisplayText(resolved.value);
|
|
64
|
+
if (sanitized === '') {
|
|
65
|
+
return degraded(trace, {
|
|
66
|
+
cause: 'sanitization',
|
|
67
|
+
expression: firstExpressionSource(params.runName),
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
value: sanitized,
|
|
73
|
+
trace,
|
|
74
|
+
...(sanitized === resolved.value
|
|
75
|
+
? {}
|
|
76
|
+
: {
|
|
77
|
+
degradation: {
|
|
78
|
+
cause: 'sanitization' as const,
|
|
79
|
+
expression: firstExpressionSource(params.runName),
|
|
80
|
+
},
|
|
81
|
+
}),
|
|
82
|
+
};
|
|
83
|
+
} catch (error) {
|
|
84
|
+
return degraded([], {
|
|
85
|
+
cause: 'evaluation_error',
|
|
86
|
+
expression:
|
|
87
|
+
error instanceof WorkflowTemplateResolutionError
|
|
88
|
+
? error.source
|
|
89
|
+
: firstExpressionSource(params.runName),
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function sanitizeWorkflowDisplayText(value: string): string {
|
|
95
|
+
const sanitized = value.replace(DISALLOWED_DISPLAY_CHARACTER_RE, ' ').trim();
|
|
96
|
+
return Array.from(sanitized).slice(0, MAX_WORKFLOW_RUN_NAME_LENGTH).join('');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function degraded(
|
|
100
|
+
trace: readonly PersistedEvaluationTraceEntry[],
|
|
101
|
+
degradation: WorkflowRunNameDegradation,
|
|
102
|
+
): ResolveWorkflowRunNameResult {
|
|
103
|
+
return {value: null, trace, degradation};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function firstExpressionSource(template: readonly ResolvedFieldSegment[]): string | undefined {
|
|
107
|
+
return template.find((segment) => segment.kind === 'deferred')?.expression.source;
|
|
108
|
+
}
|
|
@@ -27,7 +27,7 @@ export interface StepReport {
|
|
|
27
27
|
// Precomputed gate evaluation (the CEL engine runs in evaluate-gate.ts, never
|
|
28
28
|
// here). `passed`/`failed` are clean evaluations; `uncheckable` means the gate
|
|
29
29
|
// could not be evaluated (no exit code, or an evaluation error) and is treated
|
|
30
|
-
// as a plain command failure
|
|
30
|
+
// as a plain command failure. It is never a restart.
|
|
31
31
|
export type GateOutcome =
|
|
32
32
|
| {kind: 'no-gate'}
|
|
33
33
|
| {kind: 'passed'; source: string; trace?: readonly PersistedEvaluationTraceEntry[]}
|
|
@@ -55,7 +55,7 @@ export interface DecideStepTransitionInput {
|
|
|
55
55
|
// to DEFAULT_RESTART_ATTEMPT_CAP.
|
|
56
56
|
maxAttempts?: number;
|
|
57
57
|
// The gating step's own execution count (number of its attempts), used for the
|
|
58
|
-
// cap. Defaults to `reportedAttempt
|
|
58
|
+
// cap. Defaults to `reportedAttempt`: correct for a single-gate job, where the
|
|
59
59
|
// two are equal; the service passes the real count so a downstream gate in a
|
|
60
60
|
// multi-gate job isn't penalized for upstream-induced rewinds.
|
|
61
61
|
gatingAttemptCount?: number;
|
|
@@ -144,6 +144,14 @@ describe('evaluateGate', () => {
|
|
|
144
144
|
expect(evaluateGate(gate, {status: 'failed', exitCode: 0})).toMatchObject({kind: 'failed'});
|
|
145
145
|
});
|
|
146
146
|
|
|
147
|
+
test('resolves vars from the run-creation snapshot', () => {
|
|
148
|
+
const gate = readStepGate(gateConfig('vars.ENABLED == "true"'));
|
|
149
|
+
|
|
150
|
+
expect(evaluateGate(gate, {status: 'succeeded', exitCode: 0}, {ENABLED: 'true'})).toMatchObject(
|
|
151
|
+
{kind: 'passed'},
|
|
152
|
+
);
|
|
153
|
+
});
|
|
154
|
+
|
|
147
155
|
test('step.outputs gates on reported output values', () => {
|
|
148
156
|
const gate = readStepGate(gateConfig('step.outputs.pass == true'));
|
|
149
157
|
|
|
@@ -69,7 +69,11 @@ export function readStepGate(config: Record<string, unknown>): StepGate | undefi
|
|
|
69
69
|
* unless evaluation gets a budget outside the lock. The materializer only
|
|
70
70
|
* persists CEL expressions, and this evaluator only needs the validated source.
|
|
71
71
|
*/
|
|
72
|
-
export function evaluateGate(
|
|
72
|
+
export function evaluateGate(
|
|
73
|
+
gate: StepGate | undefined,
|
|
74
|
+
result: StepReport,
|
|
75
|
+
vars?: Record<string, string> | undefined,
|
|
76
|
+
): GateOutcome {
|
|
73
77
|
if (!gate?.success) return {kind: 'no-gate'};
|
|
74
78
|
const source = gate.success.source;
|
|
75
79
|
|
|
@@ -81,6 +85,7 @@ export function evaluateGate(gate: StepGate | undefined, result: StepReport): Ga
|
|
|
81
85
|
status: result.status,
|
|
82
86
|
exitCode: result.exitCode,
|
|
83
87
|
output: result.output,
|
|
88
|
+
vars,
|
|
84
89
|
});
|
|
85
90
|
const outcome = evaluatePlannedPredicateAtSite({
|
|
86
91
|
expression: gate.success,
|
|
@@ -110,6 +115,7 @@ export function evaluateGateFeedback(params: {
|
|
|
110
115
|
readonly gate: StepGate;
|
|
111
116
|
readonly result: StepReport;
|
|
112
117
|
readonly definitionId: string;
|
|
118
|
+
readonly vars?: Record<string, string> | undefined;
|
|
113
119
|
}): string {
|
|
114
120
|
const feedbackTemplate = params.gate.onFailure?.feedbackTemplate;
|
|
115
121
|
if (feedbackTemplate === undefined) {
|
|
@@ -120,6 +126,7 @@ export function evaluateGateFeedback(params: {
|
|
|
120
126
|
status: params.result.status,
|
|
121
127
|
exitCode: params.result.exitCode ?? null,
|
|
122
128
|
output: params.result.output,
|
|
129
|
+
vars: params.vars,
|
|
123
130
|
});
|
|
124
131
|
return completeStepField({
|
|
125
132
|
field: 'step.feedback',
|
|
@@ -19,7 +19,7 @@ describe('deriveInitialJobExecutionPlan', () => {
|
|
|
19
19
|
const model = buildModel({
|
|
20
20
|
jobs: {
|
|
21
21
|
deploy: {
|
|
22
|
-
|
|
22
|
+
executionName: `Deploy ${template('inputs.environment')}`,
|
|
23
23
|
runner: ['linux'],
|
|
24
24
|
runnerTemplates: [template('inputs.runner')],
|
|
25
25
|
steps: [{run: 'echo deploy'}],
|
|
@@ -47,10 +47,10 @@ describe('deriveInitialJobExecutionPlan', () => {
|
|
|
47
47
|
{
|
|
48
48
|
expression: 'inputs.environment',
|
|
49
49
|
roots: ['inputs'],
|
|
50
|
-
fillTarget: '
|
|
50
|
+
fillTarget: 'execution-creation',
|
|
51
51
|
evaluatedAt: 'execution-creation',
|
|
52
52
|
value: 'prod',
|
|
53
|
-
field: 'job.
|
|
53
|
+
field: 'job.execution_name',
|
|
54
54
|
},
|
|
55
55
|
],
|
|
56
56
|
});
|
|
@@ -86,7 +86,7 @@ describe('deriveInitialJobExecutionPlan', () => {
|
|
|
86
86
|
const model = buildModel({
|
|
87
87
|
jobs: {
|
|
88
88
|
deploy: {
|
|
89
|
-
|
|
89
|
+
executionName: `Current ${template('execution.name')}`,
|
|
90
90
|
steps: [{run: 'echo deploy'}],
|
|
91
91
|
},
|
|
92
92
|
},
|
|
@@ -136,10 +136,13 @@ function workflowRun(params: {inputs?: Record<string, unknown> | null} = {}): Wo
|
|
|
136
136
|
const now = new Date('2026-01-01T00:00:00.000Z');
|
|
137
137
|
return {
|
|
138
138
|
id: 'run-1',
|
|
139
|
+
number: 1,
|
|
139
140
|
workspaceId: 'workspace-1',
|
|
140
141
|
projectId: 'project-1',
|
|
141
142
|
definitionId: 'definition-1',
|
|
142
143
|
name: 'Test workflow',
|
|
144
|
+
workflowName: 'Test workflow',
|
|
145
|
+
nameOverride: null,
|
|
143
146
|
status: 'pending',
|
|
144
147
|
currentAttempt: 1,
|
|
145
148
|
triggerProvider: null,
|
|
@@ -55,6 +55,7 @@ export function deriveInitialJobExecutionPlan(params: {
|
|
|
55
55
|
inputs?: Record<string, unknown> | null | undefined;
|
|
56
56
|
vars?: Record<string, string> | undefined;
|
|
57
57
|
}): {
|
|
58
|
+
nameOverride: string | null;
|
|
58
59
|
name: string;
|
|
59
60
|
runner: readonly string[];
|
|
60
61
|
evaluationTrace: JobExecution['evaluationTrace'];
|
|
@@ -64,8 +65,9 @@ export function deriveInitialJobExecutionPlan(params: {
|
|
|
64
65
|
triggerPayload: params.triggerPayload,
|
|
65
66
|
inputs: params.inputs ?? null,
|
|
66
67
|
vars: params.vars,
|
|
67
|
-
|
|
68
|
+
job: {id: params.jobId, key: params.job.key, name: params.job.name ?? null},
|
|
68
69
|
sequence: params.sequence,
|
|
70
|
+
nameOverride: null,
|
|
69
71
|
executionName: params.fallbackName,
|
|
70
72
|
status: 'pending',
|
|
71
73
|
triggerEvents: [],
|
|
@@ -73,8 +75,7 @@ export function deriveInitialJobExecutionPlan(params: {
|
|
|
73
75
|
});
|
|
74
76
|
const resolvedName = resolveJobExecutionName({
|
|
75
77
|
definitionId: params.run.definitionId,
|
|
76
|
-
job: params.
|
|
77
|
-
fallbackName: params.fallbackName,
|
|
78
|
+
job: params.modelJob,
|
|
78
79
|
context: nameContext.values,
|
|
79
80
|
});
|
|
80
81
|
const runnerContext = assembleExecutionCreationContext({
|
|
@@ -82,15 +83,17 @@ export function deriveInitialJobExecutionPlan(params: {
|
|
|
82
83
|
triggerPayload: params.triggerPayload,
|
|
83
84
|
inputs: params.inputs ?? null,
|
|
84
85
|
vars: params.vars,
|
|
85
|
-
|
|
86
|
+
job: {id: params.jobId, key: params.job.key, name: params.job.name ?? null},
|
|
86
87
|
sequence: params.sequence,
|
|
87
|
-
|
|
88
|
+
nameOverride: resolvedName.nameOverride,
|
|
89
|
+
executionName: resolvedName.nameOverride ?? params.fallbackName,
|
|
88
90
|
status: 'pending',
|
|
89
91
|
triggerEvents: [],
|
|
90
92
|
priorExecutions: [],
|
|
91
93
|
});
|
|
92
94
|
return {
|
|
93
|
-
|
|
95
|
+
nameOverride: resolvedName.nameOverride,
|
|
96
|
+
name: resolvedName.nameOverride ?? params.fallbackName,
|
|
94
97
|
runner: materializeJobRunner({
|
|
95
98
|
job: params.modelJob,
|
|
96
99
|
context: runnerContext,
|
|
@@ -105,7 +108,9 @@ export function deriveJobExecutionRunner(params: {
|
|
|
105
108
|
modelJob: WorkflowModel['jobs'][number];
|
|
106
109
|
jobId: string;
|
|
107
110
|
sequence: number;
|
|
111
|
+
nameOverride: string | null;
|
|
108
112
|
executionName: string;
|
|
113
|
+
jobName?: string | null | undefined;
|
|
109
114
|
status: JobExecution['status'];
|
|
110
115
|
triggerEvents?: readonly JobExecution['triggerEvents'][number][] | undefined;
|
|
111
116
|
priorExecutions?: readonly JobExecution[] | undefined;
|
|
@@ -116,8 +121,9 @@ export function deriveJobExecutionRunner(params: {
|
|
|
116
121
|
run: params.run,
|
|
117
122
|
triggerPayload: params.run.triggerPayload,
|
|
118
123
|
inputs: params.run.inputs,
|
|
119
|
-
|
|
124
|
+
job: {id: params.jobId, key: params.modelJob.key, name: params.jobName ?? null},
|
|
120
125
|
sequence: params.sequence,
|
|
126
|
+
nameOverride: params.nameOverride,
|
|
121
127
|
executionName: params.executionName,
|
|
122
128
|
status: params.status,
|
|
123
129
|
triggerEvents: params.triggerEvents ?? [],
|
package/src/db/db.ts
CHANGED
|
@@ -7,11 +7,13 @@ import {workflowsOutbox} from './schema/outbox.js';
|
|
|
7
7
|
import {stepAttempts} from './schema/step-attempts.js';
|
|
8
8
|
import {steps} from './schema/steps.js';
|
|
9
9
|
import {workflowRunAttempts} from './schema/workflow-run-attempts.js';
|
|
10
|
+
import {workflowRunCounters} from './schema/workflow-run-counters.js';
|
|
10
11
|
import {workflowRuns} from './schema/workflow-runs.js';
|
|
11
12
|
|
|
12
13
|
export const schema = {
|
|
13
14
|
workflowRuns,
|
|
14
15
|
workflowRunAttempts,
|
|
16
|
+
workflowRunCounters,
|
|
15
17
|
jobs,
|
|
16
18
|
jobExecutions,
|
|
17
19
|
jobListenerEvents,
|
package/src/db/index.ts
CHANGED
|
@@ -23,6 +23,7 @@ export {
|
|
|
23
23
|
settleListenerJobExecution,
|
|
24
24
|
} from './job-listeners.js';
|
|
25
25
|
export {workflowsOutbox} from './schema/outbox.js';
|
|
26
|
+
export {workflowRunCounters} from './schema/workflow-run-counters.js';
|
|
26
27
|
export type {
|
|
27
28
|
BulkUpdateStepStatusesParams,
|
|
28
29
|
CancelWorkflowRunParams,
|
|
@@ -40,6 +41,10 @@ export type {
|
|
|
40
41
|
WorkflowJobExecutionDepthParams,
|
|
41
42
|
WorkflowRunAggregates,
|
|
42
43
|
WorkflowRunFilters,
|
|
44
|
+
WorkflowRunJobStatusCount,
|
|
45
|
+
WorkflowRunJobSummary,
|
|
46
|
+
WorkflowRunJobSummaryTarget,
|
|
47
|
+
WorkflowRunJobsSummary,
|
|
43
48
|
} from './workflow-runs.js';
|
|
44
49
|
export {
|
|
45
50
|
bulkUpdateStepStatuses,
|
|
@@ -72,6 +77,7 @@ export {
|
|
|
72
77
|
getWorkflowRunById,
|
|
73
78
|
getWorkflowRunDetail,
|
|
74
79
|
listRunAttempts,
|
|
80
|
+
listWorkflowRunJobSummaries,
|
|
75
81
|
listWorkflowRuns,
|
|
76
82
|
listWorkflowRunsByProject,
|
|
77
83
|
recordJobExecutionQueuedAt,
|