@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
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
import {agentInterModuleContract} from '@shipfox/api-agent-dto/inter-module';
|
|
10
10
|
import {parseWorkflowTemplate, planInterpolationField} from '@shipfox/expression';
|
|
11
11
|
import {createInterModuleKnownError} from '@shipfox/inter-module';
|
|
12
|
+
import {agentThinkingSchema} from '@shipfox/workflow-document';
|
|
12
13
|
import type {AgentDefaultsResolver} from '#core/agent-defaults.js';
|
|
13
14
|
import type {Step} from '#core/entities/step.js';
|
|
14
15
|
import {AgentConfigUnresolvableError, InterpolationUnresolvableError} from '#core/errors.js';
|
|
@@ -68,7 +69,7 @@ const resolveAgentDefaults: AgentDefaultsResolver = (params) => ({
|
|
|
68
69
|
harness: params.harness ?? 'pi',
|
|
69
70
|
provider: params.provider ?? 'openai',
|
|
70
71
|
model: params.model ?? 'gpt-5.5',
|
|
71
|
-
thinking: params.thinking ?? 'off',
|
|
72
|
+
thinking: agentThinkingSchema.safeParse(params.thinking).data ?? 'off',
|
|
72
73
|
});
|
|
73
74
|
|
|
74
75
|
function materializedIntegration(): MaterializedAgentIntegrationConfigDto {
|
|
@@ -251,6 +252,128 @@ describe('completeStepDispatchConfig', () => {
|
|
|
251
252
|
});
|
|
252
253
|
});
|
|
253
254
|
|
|
255
|
+
it('resolves a deferred working directory at step dispatch', async () => {
|
|
256
|
+
const pending = step({
|
|
257
|
+
config: {},
|
|
258
|
+
configPlan: {
|
|
259
|
+
working_directory: plannedField(template('steps.build.outputs.path')),
|
|
260
|
+
},
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
const result = await completeStepDispatchConfig({
|
|
264
|
+
step: pending,
|
|
265
|
+
context: {
|
|
266
|
+
...context,
|
|
267
|
+
values: {
|
|
268
|
+
...context.values,
|
|
269
|
+
steps: {
|
|
270
|
+
build: {
|
|
271
|
+
outputs: {path: 'packages/api'},
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
resolveAgentDefaults,
|
|
277
|
+
definitionId: 'def-1',
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
expect(result).toEqual({
|
|
281
|
+
config: {working_directory: 'packages/api'},
|
|
282
|
+
trace: [
|
|
283
|
+
{
|
|
284
|
+
expression: 'steps.build.outputs.path',
|
|
285
|
+
roots: ['steps'],
|
|
286
|
+
fillTarget: 'step-dispatch',
|
|
287
|
+
evaluatedAt: 'step-dispatch',
|
|
288
|
+
value: 'packages/api',
|
|
289
|
+
field: 'step.working_directory',
|
|
290
|
+
},
|
|
291
|
+
],
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it('resolves deferred checkout targets at step dispatch', async () => {
|
|
296
|
+
const pending = step({
|
|
297
|
+
type: 'checkout',
|
|
298
|
+
config: {
|
|
299
|
+
checkout: {
|
|
300
|
+
fetch_depth: 1,
|
|
301
|
+
permissions: {contents: 'read'},
|
|
302
|
+
persist_credentials: true,
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
configPlan: {
|
|
306
|
+
checkout: {
|
|
307
|
+
repository: plannedField(template('steps.build.outputs.repository')),
|
|
308
|
+
ref: plannedField(template('steps.build.outputs.ref')),
|
|
309
|
+
},
|
|
310
|
+
},
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
const result = await completeStepDispatchConfig({
|
|
314
|
+
step: pending,
|
|
315
|
+
context: {
|
|
316
|
+
...context,
|
|
317
|
+
values: {
|
|
318
|
+
...context.values,
|
|
319
|
+
steps: {
|
|
320
|
+
build: {
|
|
321
|
+
outputs: {repository: 'acme/api', ref: 'refs/heads/main'},
|
|
322
|
+
},
|
|
323
|
+
},
|
|
324
|
+
},
|
|
325
|
+
},
|
|
326
|
+
resolveAgentDefaults,
|
|
327
|
+
definitionId: 'def-1',
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
expect(result).toEqual({
|
|
331
|
+
config: {
|
|
332
|
+
checkout: {
|
|
333
|
+
fetch_depth: 1,
|
|
334
|
+
permissions: {contents: 'read'},
|
|
335
|
+
persist_credentials: true,
|
|
336
|
+
repository: 'acme/api',
|
|
337
|
+
ref: 'refs/heads/main',
|
|
338
|
+
},
|
|
339
|
+
},
|
|
340
|
+
trace: [
|
|
341
|
+
{
|
|
342
|
+
expression: 'steps.build.outputs.repository',
|
|
343
|
+
roots: ['steps'],
|
|
344
|
+
fillTarget: 'step-dispatch',
|
|
345
|
+
evaluatedAt: 'step-dispatch',
|
|
346
|
+
value: 'acme/api',
|
|
347
|
+
field: 'checkout.repository',
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
expression: 'steps.build.outputs.ref',
|
|
351
|
+
roots: ['steps'],
|
|
352
|
+
fillTarget: 'step-dispatch',
|
|
353
|
+
evaluatedAt: 'step-dispatch',
|
|
354
|
+
value: 'refs/heads/main',
|
|
355
|
+
field: 'checkout.ref',
|
|
356
|
+
},
|
|
357
|
+
],
|
|
358
|
+
});
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
it('rejects invalid resolved working directories', async () => {
|
|
362
|
+
const pending = step({
|
|
363
|
+
config: {working_directory: '../outside'},
|
|
364
|
+
configPlan: null,
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
await expect(
|
|
368
|
+
completeStepDispatchConfig({
|
|
369
|
+
step: pending,
|
|
370
|
+
context,
|
|
371
|
+
resolveAgentDefaults,
|
|
372
|
+
definitionId: 'def-1',
|
|
373
|
+
}),
|
|
374
|
+
).rejects.toThrow('Invalid working_directory');
|
|
375
|
+
});
|
|
376
|
+
|
|
254
377
|
it('completes deferred agent config with the resolved harness', async () => {
|
|
255
378
|
const integration = materializedIntegration();
|
|
256
379
|
const mcpServers = integrationMcpServers([integration]);
|
|
@@ -306,7 +429,7 @@ describe('completeStepDispatchConfig', () => {
|
|
|
306
429
|
configPlan: {
|
|
307
430
|
agent: {
|
|
308
431
|
harness: 'claude',
|
|
309
|
-
thinking: 'off',
|
|
432
|
+
thinking: plannedField('off'),
|
|
310
433
|
prompt: plannedField(`Review ${template('steps.build.outputs.sha')}`),
|
|
311
434
|
},
|
|
312
435
|
},
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import {WORKFLOW_MODEL_CHECKOUT_TARGET_FIELDS} from '@shipfox/api-definitions-dto';
|
|
2
|
+
import {assertWorkingDirectory} from '@shipfox/api-workflows-dto';
|
|
1
3
|
import {capTraceEntries} from '@shipfox/expression';
|
|
2
4
|
import type {AgentDefaultsResolver} from '#core/agent-defaults.js';
|
|
3
5
|
import type {PersistedEvaluationTraceEntry, Step} from '#core/entities/step.js';
|
|
4
6
|
import {completeAgentConfig} from './agent.js';
|
|
7
|
+
import {completeStepFieldWithTrace} from './fields.js';
|
|
5
8
|
import {completeRunDispatchConfig} from './run.js';
|
|
6
9
|
import type {WorkflowEvaluationContext} from './workflow-evaluation-context.js';
|
|
7
10
|
|
|
@@ -15,7 +18,10 @@ export async function completeStepDispatchConfig(params: {
|
|
|
15
18
|
readonly trace: readonly PersistedEvaluationTraceEntry[];
|
|
16
19
|
}> {
|
|
17
20
|
const plan = params.step.configPlan;
|
|
18
|
-
if (plan === null)
|
|
21
|
+
if (plan === null) {
|
|
22
|
+
assertWorkingDirectoryIfPresent(params.step.config.working_directory);
|
|
23
|
+
return {config: params.step.config, trace: []};
|
|
24
|
+
}
|
|
19
25
|
|
|
20
26
|
const config = {...params.step.config};
|
|
21
27
|
delete config.secret_bindings;
|
|
@@ -35,6 +41,82 @@ export async function completeStepDispatchConfig(params: {
|
|
|
35
41
|
definitionId: params.definitionId,
|
|
36
42
|
trace,
|
|
37
43
|
});
|
|
44
|
+
completeWorkingDirectoryConfig({
|
|
45
|
+
config,
|
|
46
|
+
plan,
|
|
47
|
+
context: params.context,
|
|
48
|
+
definitionId: params.definitionId,
|
|
49
|
+
trace,
|
|
50
|
+
});
|
|
51
|
+
completeCheckoutConfig({
|
|
52
|
+
config,
|
|
53
|
+
plan,
|
|
54
|
+
context: params.context,
|
|
55
|
+
definitionId: params.definitionId,
|
|
56
|
+
trace,
|
|
57
|
+
});
|
|
58
|
+
assertWorkingDirectoryIfPresent(config.working_directory);
|
|
38
59
|
|
|
39
60
|
return {config, trace: capTraceEntries(trace)};
|
|
40
61
|
}
|
|
62
|
+
|
|
63
|
+
function completeCheckoutConfig(params: {
|
|
64
|
+
readonly config: Record<string, unknown>;
|
|
65
|
+
readonly plan: Step['configPlan'] & object;
|
|
66
|
+
readonly context: WorkflowEvaluationContext;
|
|
67
|
+
readonly definitionId: string;
|
|
68
|
+
readonly trace: PersistedEvaluationTraceEntry[];
|
|
69
|
+
}): void {
|
|
70
|
+
const checkoutPlan = params.plan.checkout;
|
|
71
|
+
if (checkoutPlan === undefined) return;
|
|
72
|
+
|
|
73
|
+
const checkout = params.config.checkout;
|
|
74
|
+
if (checkout === null || typeof checkout !== 'object' || Array.isArray(checkout)) {
|
|
75
|
+
throw new Error('Checkout dispatch config is missing an object');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const resolvedCheckout = {...checkout} as Record<string, unknown>;
|
|
79
|
+
params.config.checkout = resolvedCheckout;
|
|
80
|
+
|
|
81
|
+
for (const [key, fieldName] of WORKFLOW_MODEL_CHECKOUT_TARGET_FIELDS) {
|
|
82
|
+
const field = checkoutPlan[key];
|
|
83
|
+
if (field === undefined) continue;
|
|
84
|
+
|
|
85
|
+
const resolved = completeStepFieldWithTrace({
|
|
86
|
+
field: fieldName,
|
|
87
|
+
template: field,
|
|
88
|
+
context: params.context,
|
|
89
|
+
definitionId: params.definitionId,
|
|
90
|
+
errorField: fieldName,
|
|
91
|
+
});
|
|
92
|
+
resolvedCheckout[key] = resolved.value;
|
|
93
|
+
params.trace.push(...resolved.trace.map((entry) => ({...entry, field: fieldName})));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function assertWorkingDirectoryIfPresent(value: unknown): void {
|
|
98
|
+
if (value !== undefined) assertWorkingDirectory(value);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function completeWorkingDirectoryConfig(params: {
|
|
102
|
+
readonly config: Record<string, unknown>;
|
|
103
|
+
readonly plan: Step['configPlan'] & object;
|
|
104
|
+
readonly context: WorkflowEvaluationContext;
|
|
105
|
+
readonly definitionId: string;
|
|
106
|
+
readonly trace: PersistedEvaluationTraceEntry[];
|
|
107
|
+
}): void {
|
|
108
|
+
const field = params.plan.working_directory;
|
|
109
|
+
if (field === undefined) return;
|
|
110
|
+
|
|
111
|
+
const resolved = completeStepFieldWithTrace({
|
|
112
|
+
field: 'step.working_directory',
|
|
113
|
+
template: field,
|
|
114
|
+
context: params.context,
|
|
115
|
+
definitionId: params.definitionId,
|
|
116
|
+
errorField: 'step.working_directory',
|
|
117
|
+
});
|
|
118
|
+
params.config.working_directory = resolved.value;
|
|
119
|
+
params.trace.push(
|
|
120
|
+
...resolved.trace.map((entry) => ({...entry, field: 'step.working_directory' as const})),
|
|
121
|
+
);
|
|
122
|
+
}
|
|
@@ -38,4 +38,11 @@ export {
|
|
|
38
38
|
resolveJobExecutionName,
|
|
39
39
|
} from './resolve-job-execution-name.js';
|
|
40
40
|
export type {WorkflowStepTemplateDiagnostic} from './resolve-step-config.js';
|
|
41
|
+
export {
|
|
42
|
+
type ResolveWorkflowRunNameResult,
|
|
43
|
+
resolveWorkflowRunName,
|
|
44
|
+
sanitizeWorkflowDisplayText,
|
|
45
|
+
type WorkflowRunNameDegradation,
|
|
46
|
+
type WorkflowRunNameResolutionCause,
|
|
47
|
+
} from './resolve-workflow-run-name.js';
|
|
41
48
|
export type {WorkflowEvaluationContext} from './workflow-evaluation-context.js';
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
AGENT_INTEGRATION_MCP_TRANSPORT,
|
|
6
6
|
} from '@shipfox/api-agent-dto';
|
|
7
7
|
import {agentInterModuleContract} from '@shipfox/api-agent-dto/inter-module';
|
|
8
|
+
import {createWorkflowExpression} from '@shipfox/expression';
|
|
8
9
|
import {createInterModuleKnownError} from '@shipfox/inter-module';
|
|
9
10
|
import type {AgentDefaultsResolver} from '#core/agent-defaults.js';
|
|
10
11
|
import type {AgentToolCatalogEntry, AgentToolMaterializationContext} from '#core/agent-tools.js';
|
|
@@ -31,6 +32,19 @@ function shellRef(name: string): string {
|
|
|
31
32
|
return `\${${name}}`;
|
|
32
33
|
}
|
|
33
34
|
|
|
35
|
+
function checkout(repository: string) {
|
|
36
|
+
return {
|
|
37
|
+
repository,
|
|
38
|
+
fetchDepth: 1,
|
|
39
|
+
permissions: {contents: 'read' as const},
|
|
40
|
+
persistCredentials: true,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function condition(source: string) {
|
|
45
|
+
return createWorkflowExpression({source, check: {mode: 'syntax'}});
|
|
46
|
+
}
|
|
47
|
+
|
|
34
48
|
function jobExecutionContext(): WorkflowEvaluationContext {
|
|
35
49
|
return {
|
|
36
50
|
site: 'execution-creation',
|
|
@@ -58,6 +72,10 @@ function jobExecutionContext(): WorkflowEvaluationContext {
|
|
|
58
72
|
event: 'pull_request_review',
|
|
59
73
|
delivery_id: 'delivery-2',
|
|
60
74
|
received_at: '2026-06-30T12:01:00.000Z',
|
|
75
|
+
project: null,
|
|
76
|
+
repository: null,
|
|
77
|
+
ref: null,
|
|
78
|
+
commit: null,
|
|
61
79
|
data: {body: 'LGTM'},
|
|
62
80
|
},
|
|
63
81
|
],
|
|
@@ -157,6 +175,232 @@ function githubAgentToolCatalog(): readonly AgentToolCatalogEntry[] {
|
|
|
157
175
|
}
|
|
158
176
|
|
|
159
177
|
describe('materializeJobExecutionSteps', () => {
|
|
178
|
+
it('resolves static job names through the job execution context', async () => {
|
|
179
|
+
const model = workflowModel({
|
|
180
|
+
jobs: {
|
|
181
|
+
review: {
|
|
182
|
+
name: 'Review',
|
|
183
|
+
steps: [{name: template('job.name'), run: 'echo review'}],
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
const job = model.jobs[0];
|
|
188
|
+
if (!job) throw new Error('Expected workflow job');
|
|
189
|
+
|
|
190
|
+
const steps = await materializeJobExecutionSteps({model, job, context: jobExecutionContext()});
|
|
191
|
+
|
|
192
|
+
expect(steps[1]).toMatchObject({
|
|
193
|
+
name: 'Review',
|
|
194
|
+
config: {run: 'echo review'},
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it('materializes checkout steps with their resolved config and default name', async () => {
|
|
199
|
+
const model = workflowModel({
|
|
200
|
+
jobs: {
|
|
201
|
+
build: {
|
|
202
|
+
steps: [
|
|
203
|
+
{
|
|
204
|
+
checkout: {
|
|
205
|
+
repository: 'acme/api',
|
|
206
|
+
ref: 'refs/heads/main',
|
|
207
|
+
fetchDepth: 0,
|
|
208
|
+
path: 'target',
|
|
209
|
+
permissions: {contents: 'write'},
|
|
210
|
+
persistCredentials: false,
|
|
211
|
+
force: true,
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
],
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
const job = model.jobs[0];
|
|
219
|
+
if (!job) throw new Error('Expected workflow job');
|
|
220
|
+
|
|
221
|
+
const steps = await materializeJobExecutionSteps({model, job, context: jobExecutionContext()});
|
|
222
|
+
|
|
223
|
+
expect(steps[0]).toMatchObject({
|
|
224
|
+
type: 'setup',
|
|
225
|
+
config: {},
|
|
226
|
+
});
|
|
227
|
+
expect(steps[1]).toEqual({
|
|
228
|
+
key: null,
|
|
229
|
+
name: 'Checkout',
|
|
230
|
+
sourceLocation: null,
|
|
231
|
+
status: 'pending',
|
|
232
|
+
type: 'checkout',
|
|
233
|
+
config: {
|
|
234
|
+
checkout: {
|
|
235
|
+
repository: 'acme/api',
|
|
236
|
+
ref: 'refs/heads/main',
|
|
237
|
+
fetch_depth: 0,
|
|
238
|
+
path: 'target',
|
|
239
|
+
permissions: {contents: 'write'},
|
|
240
|
+
persist_credentials: false,
|
|
241
|
+
force: true,
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
authoredConfig: null,
|
|
245
|
+
position: 1,
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it('places a leading checkout at the job root when its path is omitted', async () => {
|
|
250
|
+
const model = workflowModel({
|
|
251
|
+
jobs: {
|
|
252
|
+
build: {
|
|
253
|
+
steps: [{checkout: checkout('acme/api')}, {run: 'echo use checkout'}],
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
});
|
|
257
|
+
const job = model.jobs[0];
|
|
258
|
+
if (!job) throw new Error('Expected workflow job');
|
|
259
|
+
|
|
260
|
+
const steps = await materializeJobExecutionSteps({model, job, context: jobExecutionContext()});
|
|
261
|
+
|
|
262
|
+
expect(steps[0]).toMatchObject({type: 'setup', config: {}});
|
|
263
|
+
expect(steps[1]).toMatchObject({
|
|
264
|
+
type: 'checkout',
|
|
265
|
+
config: {checkout: {repository: 'acme/api', path: '.'}},
|
|
266
|
+
});
|
|
267
|
+
expect(steps[2]).toMatchObject({type: 'run', config: {run: 'echo use checkout'}});
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it('keeps the implicit checkout when an explicit checkout is not leading', async () => {
|
|
271
|
+
const model = workflowModel({
|
|
272
|
+
jobs: {
|
|
273
|
+
build: {
|
|
274
|
+
steps: [
|
|
275
|
+
{run: 'echo route'},
|
|
276
|
+
{checkout: checkout('acme/api')},
|
|
277
|
+
{run: 'echo use checkout'},
|
|
278
|
+
],
|
|
279
|
+
},
|
|
280
|
+
},
|
|
281
|
+
});
|
|
282
|
+
const job = model.jobs[0];
|
|
283
|
+
if (!job) throw new Error('Expected workflow job');
|
|
284
|
+
|
|
285
|
+
const steps = await materializeJobExecutionSteps({model, job, context: jobExecutionContext()});
|
|
286
|
+
|
|
287
|
+
expect(steps[0]).toMatchObject({
|
|
288
|
+
type: 'setup',
|
|
289
|
+
config: {
|
|
290
|
+
checkout: {
|
|
291
|
+
permissions: {contents: 'read'},
|
|
292
|
+
persist_credentials: true,
|
|
293
|
+
},
|
|
294
|
+
},
|
|
295
|
+
});
|
|
296
|
+
expect(steps[2]).toMatchObject({
|
|
297
|
+
type: 'checkout',
|
|
298
|
+
config: {
|
|
299
|
+
checkout: {
|
|
300
|
+
repository: 'acme/api',
|
|
301
|
+
fetch_depth: 1,
|
|
302
|
+
permissions: {contents: 'read'},
|
|
303
|
+
persist_credentials: true,
|
|
304
|
+
},
|
|
305
|
+
},
|
|
306
|
+
});
|
|
307
|
+
expect(steps[2]?.config.checkout).not.toHaveProperty('path');
|
|
308
|
+
expect(steps[3]).toMatchObject({type: 'run', config: {run: 'echo use checkout'}});
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
it('omits the implicit checkout when the job opts out', async () => {
|
|
312
|
+
const model = workflowModel({
|
|
313
|
+
jobs: {
|
|
314
|
+
build: {
|
|
315
|
+
checkout: false,
|
|
316
|
+
steps: [{run: 'echo no repository'}],
|
|
317
|
+
},
|
|
318
|
+
},
|
|
319
|
+
});
|
|
320
|
+
const job = model.jobs[0];
|
|
321
|
+
if (!job) throw new Error('Expected workflow job');
|
|
322
|
+
|
|
323
|
+
const steps = await materializeJobExecutionSteps({model, job, context: jobExecutionContext()});
|
|
324
|
+
|
|
325
|
+
expect(steps[0]).toMatchObject({type: 'setup', config: {}});
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it('does not restore the implicit checkout for a leading checkout with a false if', async () => {
|
|
329
|
+
const model = workflowModel({
|
|
330
|
+
jobs: {
|
|
331
|
+
build: {
|
|
332
|
+
steps: [{if: condition('false'), checkout: checkout('acme/api')}],
|
|
333
|
+
},
|
|
334
|
+
},
|
|
335
|
+
});
|
|
336
|
+
const job = model.jobs[0];
|
|
337
|
+
if (!job) throw new Error('Expected workflow job');
|
|
338
|
+
|
|
339
|
+
const steps = await materializeJobExecutionSteps({model, job, context: jobExecutionContext()});
|
|
340
|
+
|
|
341
|
+
expect(steps[0]).toMatchObject({type: 'setup', config: {}});
|
|
342
|
+
expect(steps[1]).toMatchObject({
|
|
343
|
+
type: 'checkout',
|
|
344
|
+
condition: expect.any(Object),
|
|
345
|
+
config: {checkout: {repository: 'acme/api', path: '.'}},
|
|
346
|
+
});
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
it('propagates a non-default job checkout policy into the setup step config', async () => {
|
|
350
|
+
const model = workflowModel({
|
|
351
|
+
jobs: {
|
|
352
|
+
build: {
|
|
353
|
+
checkout: {
|
|
354
|
+
permissions: {contents: 'write'},
|
|
355
|
+
persistCredentials: false,
|
|
356
|
+
},
|
|
357
|
+
steps: [{run: 'echo hello'}],
|
|
358
|
+
},
|
|
359
|
+
},
|
|
360
|
+
});
|
|
361
|
+
const job = model.jobs[0];
|
|
362
|
+
if (!job) throw new Error('Expected workflow job');
|
|
363
|
+
|
|
364
|
+
const steps = await materializeJobExecutionSteps({model, job, context: jobExecutionContext()});
|
|
365
|
+
|
|
366
|
+
expect(steps[0]).toMatchObject({
|
|
367
|
+
type: 'setup',
|
|
368
|
+
config: {
|
|
369
|
+
checkout: {
|
|
370
|
+
permissions: {contents: 'write'},
|
|
371
|
+
persist_credentials: false,
|
|
372
|
+
},
|
|
373
|
+
},
|
|
374
|
+
});
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
it('falls back to the default checkout policy for legacy jobs', async () => {
|
|
378
|
+
const model = workflowModel({
|
|
379
|
+
jobs: {
|
|
380
|
+
build: {steps: [{run: 'echo hello'}]},
|
|
381
|
+
},
|
|
382
|
+
});
|
|
383
|
+
const job = model.jobs[0];
|
|
384
|
+
if (!job) throw new Error('Expected workflow job');
|
|
385
|
+
const legacyJob = {...job, checkout: undefined} as unknown as typeof job;
|
|
386
|
+
|
|
387
|
+
const steps = await materializeJobExecutionSteps({
|
|
388
|
+
model,
|
|
389
|
+
job: legacyJob,
|
|
390
|
+
context: jobExecutionContext(),
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
expect(steps[0]).toMatchObject({
|
|
394
|
+
type: 'setup',
|
|
395
|
+
config: {
|
|
396
|
+
checkout: {
|
|
397
|
+
permissions: {contents: 'read'},
|
|
398
|
+
persist_credentials: true,
|
|
399
|
+
},
|
|
400
|
+
},
|
|
401
|
+
});
|
|
402
|
+
});
|
|
403
|
+
|
|
160
404
|
it('prepends setup and resolves job-execution context fields', async () => {
|
|
161
405
|
const model = workflowModel({
|
|
162
406
|
jobs: {
|
|
@@ -183,7 +427,12 @@ describe('materializeJobExecutionSteps', () => {
|
|
|
183
427
|
sourceLocation: null,
|
|
184
428
|
status: 'pending',
|
|
185
429
|
type: 'setup',
|
|
186
|
-
config: {
|
|
430
|
+
config: {
|
|
431
|
+
checkout: {
|
|
432
|
+
permissions: {contents: 'read'},
|
|
433
|
+
persist_credentials: true,
|
|
434
|
+
},
|
|
435
|
+
},
|
|
187
436
|
authoredConfig: null,
|
|
188
437
|
position: 0,
|
|
189
438
|
},
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import {DEFAULT_JOB_CHECKOUT, type WorkflowModel} from '@shipfox/api-definitions-dto';
|
|
2
2
|
import type {WorkflowExpression} from '@shipfox/expression';
|
|
3
3
|
import type {AgentDefaultsResolver} from '#core/agent-defaults.js';
|
|
4
4
|
import type {
|
|
@@ -41,15 +41,14 @@ export interface MaterializeJobExecutionStepsParams {
|
|
|
41
41
|
|
|
42
42
|
// Synthetic "Set up job" step prepended when a job execution's steps are materialized.
|
|
43
43
|
// The runner prepares the workspace here; failures report through the normal step
|
|
44
|
-
// protocol instead of hanging the job until the lease/timeout fires. Its config
|
|
45
|
-
// credential
|
|
46
|
-
const SETUP_STEP: MaterializedWorkflowStep = {
|
|
44
|
+
// protocol instead of hanging the job until the lease/timeout fires. Its config carries
|
|
45
|
+
// checkout policy, never credential material.
|
|
46
|
+
const SETUP_STEP: Omit<MaterializedWorkflowStep, 'config'> = {
|
|
47
47
|
key: null,
|
|
48
48
|
name: 'Set up job',
|
|
49
49
|
sourceLocation: null,
|
|
50
50
|
status: 'pending',
|
|
51
51
|
type: 'setup',
|
|
52
|
-
config: {},
|
|
53
52
|
authoredConfig: null,
|
|
54
53
|
position: 0,
|
|
55
54
|
};
|
|
@@ -68,11 +67,13 @@ export async function materializeJobExecutionSteps(
|
|
|
68
67
|
} = params;
|
|
69
68
|
|
|
70
69
|
return [
|
|
71
|
-
|
|
70
|
+
setupStepForJob(job),
|
|
72
71
|
...(await Promise.all(
|
|
73
72
|
job.steps.map(async (step, stepPosition) => {
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
const stepContext = {
|
|
74
|
+
...context.values,
|
|
75
|
+
job: {key: job.key, name: job.name ?? job.key},
|
|
76
|
+
};
|
|
76
77
|
const resolved = await resolveStepConfig({
|
|
77
78
|
jobKey: job.key,
|
|
78
79
|
step,
|
|
@@ -93,7 +94,11 @@ export async function materializeJobExecutionSteps(
|
|
|
93
94
|
sourceLocation: step.sourceLocation ?? null,
|
|
94
95
|
status: 'pending' as const,
|
|
95
96
|
type: step.kind,
|
|
96
|
-
config:
|
|
97
|
+
config: materializedStepConfig({
|
|
98
|
+
config: resolved.config,
|
|
99
|
+
step,
|
|
100
|
+
stepPosition,
|
|
101
|
+
}),
|
|
97
102
|
...(step.if === undefined ? {} : {condition: step.if}),
|
|
98
103
|
authoredConfig: resolved.authoredConfig,
|
|
99
104
|
...materializedConfigPlan(resolved.configPlan, resolved.trace),
|
|
@@ -105,6 +110,45 @@ export async function materializeJobExecutionSteps(
|
|
|
105
110
|
];
|
|
106
111
|
}
|
|
107
112
|
|
|
113
|
+
function setupStepForJob(job: WorkflowModelJob): MaterializedWorkflowStep {
|
|
114
|
+
if (job.checkout === false || job.steps[0]?.kind === 'checkout') {
|
|
115
|
+
return {...SETUP_STEP, config: {}};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const checkout = job.checkout ?? DEFAULT_JOB_CHECKOUT;
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
...SETUP_STEP,
|
|
122
|
+
config: {
|
|
123
|
+
checkout: {
|
|
124
|
+
permissions: checkout.permissions,
|
|
125
|
+
persist_credentials: checkout.persistCredentials,
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function materializedStepConfig(params: {
|
|
132
|
+
readonly config: Readonly<Record<string, unknown>>;
|
|
133
|
+
readonly step: WorkflowModelStep;
|
|
134
|
+
readonly stepPosition: number;
|
|
135
|
+
}): Readonly<Record<string, unknown>> {
|
|
136
|
+
if (
|
|
137
|
+
params.step.kind !== 'checkout' ||
|
|
138
|
+
params.stepPosition !== 0 ||
|
|
139
|
+
params.step.checkout.path !== undefined
|
|
140
|
+
) {
|
|
141
|
+
return params.config;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const checkout = params.config.checkout;
|
|
145
|
+
if (checkout === null || typeof checkout !== 'object' || Array.isArray(checkout)) {
|
|
146
|
+
throw new Error('Checkout materialization config is missing an object');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return {...params.config, checkout: {...checkout, path: '.'}};
|
|
150
|
+
}
|
|
151
|
+
|
|
108
152
|
function materializedConfigPlan(
|
|
109
153
|
configPlan: StepConfigDispatchPlan | null,
|
|
110
154
|
trace: NonNullable<StepConfigDispatchPlan['trace']>,
|
|
@@ -114,13 +158,16 @@ function materializedConfigPlan(
|
|
|
114
158
|
}
|
|
115
159
|
|
|
116
160
|
function stepDisplayName(step: WorkflowModelStep): string {
|
|
117
|
-
|
|
161
|
+
const {kind} = step;
|
|
162
|
+
switch (kind) {
|
|
118
163
|
case 'run':
|
|
119
164
|
return firstLine(step.command.value);
|
|
120
165
|
case 'agent':
|
|
121
166
|
return step.model === undefined
|
|
122
167
|
? firstLine(step.prompt)
|
|
123
168
|
: `${step.model} · ${firstLine(step.prompt)}`;
|
|
169
|
+
case 'checkout':
|
|
170
|
+
return 'Checkout';
|
|
124
171
|
default:
|
|
125
172
|
return assertNever(step);
|
|
126
173
|
}
|