@shipfox/api-workflows 11.0.0 → 12.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +8 -8
- package/CHANGELOG.md +100 -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/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/checkout-token.test.ts +357 -142
- package/src/presentation/routes/checkout-token.ts +56 -26
- package/src/presentation/routes/get-run.test.ts +2 -0
- 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-runs.test.ts +57 -0
- 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/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,6 @@
|
|
|
1
|
+
import type {WorkflowFieldTemplate} from '@shipfox/api-definitions-dto';
|
|
1
2
|
import {WORKFLOWS_WORKFLOW_RUN_ATTEMPT_CREATED} from '@shipfox/api-workflows-dto';
|
|
3
|
+
import {parseWorkflowTemplate, planInterpolationField} from '@shipfox/expression';
|
|
2
4
|
import {and, eq, sql} from 'drizzle-orm';
|
|
3
5
|
import type {AgentDefaultsResolver} from '#core/agent-defaults.js';
|
|
4
6
|
import {InterpolationUnresolvableError} from '#core/errors.js';
|
|
@@ -9,9 +11,11 @@ import {buildModel, expression, shellRef, template} from '#test/helpers/workflow
|
|
|
9
11
|
import {workflowModel} from '#test/index.js';
|
|
10
12
|
import {db} from '../db.js';
|
|
11
13
|
import {workflowsOutbox} from '../schema/outbox.js';
|
|
14
|
+
import {workflowRunCounters} from '../schema/workflow-run-counters.js';
|
|
12
15
|
import {workflowRuns} from '../schema/workflow-runs.js';
|
|
13
16
|
import {
|
|
14
17
|
createWorkflowRun,
|
|
18
|
+
evaluateJobActivations,
|
|
15
19
|
getJobExecutionsByJobId,
|
|
16
20
|
getJobsByWorkflowRunId,
|
|
17
21
|
getStepsByJobId,
|
|
@@ -23,6 +27,15 @@ import {
|
|
|
23
27
|
updateJobExecutionStatus,
|
|
24
28
|
} from '../workflow-runs.js';
|
|
25
29
|
|
|
30
|
+
function checkoutTemplate(source: string): WorkflowFieldTemplate {
|
|
31
|
+
const result = planInterpolationField({
|
|
32
|
+
field: 'checkout.repository',
|
|
33
|
+
segments: parseWorkflowTemplate(source),
|
|
34
|
+
});
|
|
35
|
+
if (!result.ok) throw new Error('Expected valid checkout template');
|
|
36
|
+
return result.plan.field.segments;
|
|
37
|
+
}
|
|
38
|
+
|
|
26
39
|
describe('workflow run queries', () => {
|
|
27
40
|
let workspaceId: string;
|
|
28
41
|
let projectId: string;
|
|
@@ -35,6 +48,219 @@ describe('workflow run queries', () => {
|
|
|
35
48
|
});
|
|
36
49
|
|
|
37
50
|
describe('createWorkflowRun', () => {
|
|
51
|
+
test('persists normalized integration trigger facts alongside the payload', async () => {
|
|
52
|
+
const triggerConnectionId = crypto.randomUUID();
|
|
53
|
+
const integrations = {
|
|
54
|
+
resolveTriggerReference: vi.fn().mockResolvedValue({
|
|
55
|
+
externalRepositoryId: 'github:42',
|
|
56
|
+
ref: 'refs/heads/main',
|
|
57
|
+
commit: 'a'.repeat(40),
|
|
58
|
+
}),
|
|
59
|
+
resolveSourceRepository: vi.fn().mockResolvedValue({
|
|
60
|
+
connection: {id: triggerConnectionId, provider: 'github', slug: 'github-main'},
|
|
61
|
+
repository: {
|
|
62
|
+
externalRepositoryId: 'github:42',
|
|
63
|
+
owner: 'acme',
|
|
64
|
+
name: 'api',
|
|
65
|
+
fullName: 'acme/api',
|
|
66
|
+
defaultBranch: 'main',
|
|
67
|
+
visibility: 'private' as const,
|
|
68
|
+
cloneUrl: 'https://github.com/acme/api.git',
|
|
69
|
+
htmlUrl: 'https://github.com/acme/api',
|
|
70
|
+
},
|
|
71
|
+
}),
|
|
72
|
+
} as never;
|
|
73
|
+
const projects = {
|
|
74
|
+
getProjectBySource: vi.fn().mockResolvedValue({project: {id: 'project-1'}}),
|
|
75
|
+
} as never;
|
|
76
|
+
|
|
77
|
+
const run = await createWorkflowRun({
|
|
78
|
+
workspaceId,
|
|
79
|
+
projectId,
|
|
80
|
+
definitionId,
|
|
81
|
+
model: buildModel(),
|
|
82
|
+
triggerConnectionId,
|
|
83
|
+
triggerPayload: {
|
|
84
|
+
provider: 'github',
|
|
85
|
+
source: 'github-main',
|
|
86
|
+
event: 'push',
|
|
87
|
+
deliveryId: 'delivery-1',
|
|
88
|
+
data: {ref: 'refs/heads/main', headCommitSha: 'a'.repeat(40)},
|
|
89
|
+
},
|
|
90
|
+
integrations,
|
|
91
|
+
projects,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const triggerReference = {
|
|
95
|
+
project: {id: 'project-1'},
|
|
96
|
+
repository: 'acme/api',
|
|
97
|
+
ref: 'refs/heads/main',
|
|
98
|
+
commit: 'a'.repeat(40),
|
|
99
|
+
};
|
|
100
|
+
expect(run.triggerReference).toEqual(triggerReference);
|
|
101
|
+
await expect(
|
|
102
|
+
db()
|
|
103
|
+
.select({triggerReference: workflowRuns.triggerReference})
|
|
104
|
+
.from(workflowRuns)
|
|
105
|
+
.where(eq(workflowRuns.id, run.id)),
|
|
106
|
+
).resolves.toEqual([{triggerReference}]);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test('creates a run when trigger enrichment fails', async () => {
|
|
110
|
+
const triggerConnectionId = crypto.randomUUID();
|
|
111
|
+
const integrations = {
|
|
112
|
+
resolveTriggerReference: vi.fn().mockResolvedValue({
|
|
113
|
+
externalRepositoryId: 'github:42',
|
|
114
|
+
ref: 'refs/heads/main',
|
|
115
|
+
commit: 'a'.repeat(40),
|
|
116
|
+
}),
|
|
117
|
+
resolveSourceRepository: vi.fn().mockRejectedValue(new Error('source unavailable')),
|
|
118
|
+
} as never;
|
|
119
|
+
const projects = {
|
|
120
|
+
getProjectBySource: vi.fn().mockResolvedValue({project: {id: 'project-1'}}),
|
|
121
|
+
} as never;
|
|
122
|
+
|
|
123
|
+
const run = await createWorkflowRun({
|
|
124
|
+
workspaceId,
|
|
125
|
+
projectId,
|
|
126
|
+
definitionId,
|
|
127
|
+
model: buildModel(),
|
|
128
|
+
triggerConnectionId,
|
|
129
|
+
triggerPayload: {
|
|
130
|
+
provider: 'github',
|
|
131
|
+
source: 'github-main',
|
|
132
|
+
event: 'push',
|
|
133
|
+
deliveryId: 'delivery-1',
|
|
134
|
+
data: {ref: 'refs/heads/main', headCommitSha: 'a'.repeat(40)},
|
|
135
|
+
},
|
|
136
|
+
integrations,
|
|
137
|
+
projects,
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
expect(run.triggerReference).toBeNull();
|
|
141
|
+
await expect(
|
|
142
|
+
db()
|
|
143
|
+
.select({triggerReference: workflowRuns.triggerReference})
|
|
144
|
+
.from(workflowRuns)
|
|
145
|
+
.where(eq(workflowRuns.id, run.id)),
|
|
146
|
+
).resolves.toEqual([{triggerReference: null}]);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test('resolves and persists a dynamic run name while preserving the static snapshot', async () => {
|
|
150
|
+
const run = await createWorkflowRun({
|
|
151
|
+
workspaceId,
|
|
152
|
+
projectId,
|
|
153
|
+
definitionId,
|
|
154
|
+
model: buildModel({
|
|
155
|
+
name: 'Deploy application',
|
|
156
|
+
runName: `Deploy ${template('event.environment')}`,
|
|
157
|
+
}),
|
|
158
|
+
triggerPayload: {
|
|
159
|
+
source: 'github',
|
|
160
|
+
event: 'deployment',
|
|
161
|
+
deliveryId: 'delivery-1',
|
|
162
|
+
data: {environment: 'production'},
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
expect(run).toMatchObject({
|
|
167
|
+
name: 'Deploy production',
|
|
168
|
+
workflowName: 'Deploy application',
|
|
169
|
+
nameOverride: 'Deploy production',
|
|
170
|
+
});
|
|
171
|
+
await expect(getWorkflowRunById(run.id)).resolves.toMatchObject({
|
|
172
|
+
name: 'Deploy production',
|
|
173
|
+
workflowName: 'Deploy application',
|
|
174
|
+
nameOverride: 'Deploy production',
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
test('loads referenced variables before resolving the run name', async () => {
|
|
179
|
+
const secrets = createTestSecretsClient();
|
|
180
|
+
await secrets.setSecrets({
|
|
181
|
+
workspaceId,
|
|
182
|
+
projectId,
|
|
183
|
+
namespace: '',
|
|
184
|
+
values: {ENVIRONMENT: 'staging'},
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
const run = await createWorkflowRun({
|
|
188
|
+
workspaceId,
|
|
189
|
+
projectId,
|
|
190
|
+
definitionId,
|
|
191
|
+
model: buildModel({runName: `Deploy ${template('vars.ENVIRONMENT')}`}),
|
|
192
|
+
triggerPayload: {
|
|
193
|
+
source: 'manual',
|
|
194
|
+
event: 'fire',
|
|
195
|
+
subscriptionId: crypto.randomUUID(),
|
|
196
|
+
userId: crypto.randomUUID(),
|
|
197
|
+
},
|
|
198
|
+
secrets,
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
expect(run).toMatchObject({
|
|
202
|
+
name: 'Deploy staging',
|
|
203
|
+
workflowName: 'Test Workflow',
|
|
204
|
+
nameOverride: 'Deploy staging',
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
test('creates the run when only the run name references variables without a secrets client', async () => {
|
|
209
|
+
const run = await createWorkflowRun({
|
|
210
|
+
workspaceId,
|
|
211
|
+
projectId,
|
|
212
|
+
definitionId,
|
|
213
|
+
model: buildModel({
|
|
214
|
+
name: 'Deploy application',
|
|
215
|
+
runName: template('vars.ENVIRONMENT'),
|
|
216
|
+
}),
|
|
217
|
+
triggerPayload: {
|
|
218
|
+
source: 'manual',
|
|
219
|
+
event: 'fire',
|
|
220
|
+
subscriptionId: crypto.randomUUID(),
|
|
221
|
+
userId: crypto.randomUUID(),
|
|
222
|
+
},
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
expect(run).toMatchObject({
|
|
226
|
+
name: 'Deploy application',
|
|
227
|
+
workflowName: 'Deploy application',
|
|
228
|
+
nameOverride: null,
|
|
229
|
+
});
|
|
230
|
+
await expect(getJobsByWorkflowRunId(run.id)).resolves.toHaveLength(1);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
test('falls back to the static name when run-name resolution cannot produce a value', async () => {
|
|
234
|
+
const run = await createWorkflowRun({
|
|
235
|
+
workspaceId,
|
|
236
|
+
projectId,
|
|
237
|
+
definitionId,
|
|
238
|
+
model: buildModel({
|
|
239
|
+
name: 'Deploy application',
|
|
240
|
+
runName: template('event.environment'),
|
|
241
|
+
}),
|
|
242
|
+
triggerPayload: {
|
|
243
|
+
source: 'github',
|
|
244
|
+
event: 'deployment',
|
|
245
|
+
deliveryId: 'delivery-1',
|
|
246
|
+
data: {},
|
|
247
|
+
},
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
expect(run).toMatchObject({
|
|
251
|
+
name: 'Deploy application',
|
|
252
|
+
workflowName: 'Deploy application',
|
|
253
|
+
nameOverride: null,
|
|
254
|
+
});
|
|
255
|
+
await expect(
|
|
256
|
+
db()
|
|
257
|
+
.select({name: workflowRuns.name, workflowName: workflowRuns.workflowName})
|
|
258
|
+
.from(workflowRuns)
|
|
259
|
+
.where(eq(workflowRuns.id, run.id)),
|
|
260
|
+
).resolves.toEqual([{name: null, workflowName: 'Deploy application'}]);
|
|
261
|
+
await expect(getJobsByWorkflowRunId(run.id)).resolves.toHaveLength(1);
|
|
262
|
+
});
|
|
263
|
+
|
|
38
264
|
test('inserts run, jobs, and steps atomically', async () => {
|
|
39
265
|
const run = await createWorkflowRun({
|
|
40
266
|
workspaceId,
|
|
@@ -53,6 +279,7 @@ describe('workflow run queries', () => {
|
|
|
53
279
|
expect(run.id).toBeDefined();
|
|
54
280
|
expect(run.projectId).toBe(projectId);
|
|
55
281
|
expect(run.definitionId).toBe(definitionId);
|
|
282
|
+
expect(run.number).toBe(1);
|
|
56
283
|
expect(run.status).toBe('pending');
|
|
57
284
|
expect(run.triggerProvider).toBeNull();
|
|
58
285
|
expect(run.triggerPayload).toMatchObject({source: 'manual', event: 'fire'});
|
|
@@ -75,7 +302,8 @@ describe('workflow run queries', () => {
|
|
|
75
302
|
expect(jobExecutions[0]).toMatchObject({
|
|
76
303
|
jobId: runJobs[0]?.id,
|
|
77
304
|
sequence: 1,
|
|
78
|
-
|
|
305
|
+
nameOverride: null,
|
|
306
|
+
name: 'build',
|
|
79
307
|
runner: ['ubuntu-latest'],
|
|
80
308
|
evaluationTrace: null,
|
|
81
309
|
});
|
|
@@ -88,11 +316,147 @@ describe('workflow run queries', () => {
|
|
|
88
316
|
type: 'setup',
|
|
89
317
|
name: 'Set up job',
|
|
90
318
|
position: 0,
|
|
91
|
-
config: {
|
|
319
|
+
config: {
|
|
320
|
+
checkout: {
|
|
321
|
+
permissions: {contents: 'read'},
|
|
322
|
+
persist_credentials: true,
|
|
323
|
+
},
|
|
324
|
+
},
|
|
92
325
|
});
|
|
93
326
|
expect(jobSteps[1]).toMatchObject({position: 1, config: {run: 'echo hello'}});
|
|
94
327
|
});
|
|
95
328
|
|
|
329
|
+
test('allocates distinct consecutive numbers for concurrent creations of one definition', async () => {
|
|
330
|
+
const [first, second] = await Promise.all([
|
|
331
|
+
createWorkflowRun({
|
|
332
|
+
workspaceId,
|
|
333
|
+
projectId,
|
|
334
|
+
definitionId,
|
|
335
|
+
model: buildModel({name: 'First'}),
|
|
336
|
+
triggerPayload: {
|
|
337
|
+
source: 'manual',
|
|
338
|
+
event: 'fire',
|
|
339
|
+
subscriptionId: crypto.randomUUID(),
|
|
340
|
+
userId: crypto.randomUUID(),
|
|
341
|
+
},
|
|
342
|
+
}),
|
|
343
|
+
createWorkflowRun({
|
|
344
|
+
workspaceId,
|
|
345
|
+
projectId,
|
|
346
|
+
definitionId,
|
|
347
|
+
model: buildModel({name: 'Second'}),
|
|
348
|
+
triggerPayload: {
|
|
349
|
+
source: 'manual',
|
|
350
|
+
event: 'fire',
|
|
351
|
+
subscriptionId: crypto.randomUUID(),
|
|
352
|
+
userId: crypto.randomUUID(),
|
|
353
|
+
},
|
|
354
|
+
}),
|
|
355
|
+
]);
|
|
356
|
+
|
|
357
|
+
expect([first.number, second.number].sort((a, b) => a - b)).toEqual([1, 2]);
|
|
358
|
+
const [counter] = await db()
|
|
359
|
+
.select()
|
|
360
|
+
.from(workflowRunCounters)
|
|
361
|
+
.where(eq(workflowRunCounters.definitionId, definitionId));
|
|
362
|
+
expect(counter?.nextNumber).toBe(3);
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
test('makes the allocated number available to run-creation expressions', async () => {
|
|
366
|
+
const run = await createWorkflowRun({
|
|
367
|
+
workspaceId,
|
|
368
|
+
projectId,
|
|
369
|
+
definitionId,
|
|
370
|
+
model: buildModel({
|
|
371
|
+
jobs: {
|
|
372
|
+
build: {
|
|
373
|
+
executionName: `Build #${template('run.number')}`,
|
|
374
|
+
steps: [{run: 'echo build'}],
|
|
375
|
+
},
|
|
376
|
+
},
|
|
377
|
+
}),
|
|
378
|
+
triggerPayload: {
|
|
379
|
+
source: 'manual',
|
|
380
|
+
event: 'fire',
|
|
381
|
+
subscriptionId: crypto.randomUUID(),
|
|
382
|
+
userId: crypto.randomUUID(),
|
|
383
|
+
},
|
|
384
|
+
});
|
|
385
|
+
const [job] = await getJobsByWorkflowRunId(run.id);
|
|
386
|
+
if (!job) throw new Error('Expected workflow job');
|
|
387
|
+
const [execution] = await getJobExecutionsByJobId(job.id);
|
|
388
|
+
|
|
389
|
+
expect(execution?.name).toBe('Build #1');
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
test('rolls back number allocation when creation rolls back', async () => {
|
|
393
|
+
await expect(
|
|
394
|
+
createWorkflowRun({
|
|
395
|
+
workspaceId,
|
|
396
|
+
projectId,
|
|
397
|
+
definitionId,
|
|
398
|
+
model: buildModel({env: {TOKEN: template('vars.TOKEN')}}),
|
|
399
|
+
triggerPayload: {
|
|
400
|
+
source: 'manual',
|
|
401
|
+
event: 'fire',
|
|
402
|
+
subscriptionId: crypto.randomUUID(),
|
|
403
|
+
userId: crypto.randomUUID(),
|
|
404
|
+
},
|
|
405
|
+
}),
|
|
406
|
+
).rejects.toThrow('Secrets client is not configured.');
|
|
407
|
+
|
|
408
|
+
const run = await createWorkflowRun({
|
|
409
|
+
workspaceId,
|
|
410
|
+
projectId,
|
|
411
|
+
definitionId,
|
|
412
|
+
model: buildModel(),
|
|
413
|
+
triggerPayload: {
|
|
414
|
+
source: 'manual',
|
|
415
|
+
event: 'fire',
|
|
416
|
+
subscriptionId: crypto.randomUUID(),
|
|
417
|
+
userId: crypto.randomUUID(),
|
|
418
|
+
},
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
expect(run.number).toBe(1);
|
|
422
|
+
const [counter] = await db()
|
|
423
|
+
.select()
|
|
424
|
+
.from(workflowRunCounters)
|
|
425
|
+
.where(eq(workflowRunCounters.definitionId, definitionId));
|
|
426
|
+
expect(counter?.nextNumber).toBe(2);
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
test('numbers definitions independently within one project', async () => {
|
|
430
|
+
const otherDefinitionId = crypto.randomUUID();
|
|
431
|
+
const first = await createWorkflowRun({
|
|
432
|
+
workspaceId,
|
|
433
|
+
projectId,
|
|
434
|
+
definitionId,
|
|
435
|
+
model: buildModel(),
|
|
436
|
+
triggerPayload: {
|
|
437
|
+
source: 'manual',
|
|
438
|
+
event: 'fire',
|
|
439
|
+
subscriptionId: crypto.randomUUID(),
|
|
440
|
+
userId: crypto.randomUUID(),
|
|
441
|
+
},
|
|
442
|
+
});
|
|
443
|
+
const second = await createWorkflowRun({
|
|
444
|
+
workspaceId,
|
|
445
|
+
projectId,
|
|
446
|
+
definitionId: otherDefinitionId,
|
|
447
|
+
model: buildModel(),
|
|
448
|
+
triggerPayload: {
|
|
449
|
+
source: 'manual',
|
|
450
|
+
event: 'fire',
|
|
451
|
+
subscriptionId: crypto.randomUUID(),
|
|
452
|
+
userId: crypto.randomUUID(),
|
|
453
|
+
},
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
expect(first.number).toBe(1);
|
|
457
|
+
expect(second.number).toBe(1);
|
|
458
|
+
});
|
|
459
|
+
|
|
96
460
|
async function createJobOutputRun() {
|
|
97
461
|
const run = await createWorkflowRun({
|
|
98
462
|
workspaceId,
|
|
@@ -219,7 +583,7 @@ describe('workflow run queries', () => {
|
|
|
219
583
|
env: {RUN_ID: template('run.id')},
|
|
220
584
|
jobs: {
|
|
221
585
|
build: {
|
|
222
|
-
|
|
586
|
+
executionName: `Build ${template('event.ref')}`,
|
|
223
587
|
steps: [
|
|
224
588
|
{
|
|
225
589
|
run: 'npm test',
|
|
@@ -257,15 +621,122 @@ describe('workflow run queries', () => {
|
|
|
257
621
|
{
|
|
258
622
|
expression: 'event.ref',
|
|
259
623
|
roots: ['event'],
|
|
260
|
-
fillTarget: '
|
|
624
|
+
fillTarget: 'execution-creation',
|
|
261
625
|
evaluatedAt: 'execution-creation',
|
|
262
626
|
value: 'refs/heads/main',
|
|
263
|
-
field: 'job.
|
|
627
|
+
field: 'job.execution_name',
|
|
264
628
|
},
|
|
265
629
|
],
|
|
266
630
|
});
|
|
267
631
|
});
|
|
268
632
|
|
|
633
|
+
test('loads predicate vars once and persists the run-creation snapshot', async () => {
|
|
634
|
+
const secrets = createTestSecretsClient();
|
|
635
|
+
const values = {
|
|
636
|
+
JOB_IF: 'true',
|
|
637
|
+
JOB_SUCCESS: 'true',
|
|
638
|
+
STEP_IF: 'true',
|
|
639
|
+
GATE_SUCCESS: 'true',
|
|
640
|
+
LISTENER_ON: 'true',
|
|
641
|
+
LISTENER_UNTIL: 'true',
|
|
642
|
+
};
|
|
643
|
+
await secrets.setSecrets({workspaceId, projectId, values});
|
|
644
|
+
|
|
645
|
+
const model = workflowModel({
|
|
646
|
+
jobs: {
|
|
647
|
+
build: {
|
|
648
|
+
if: 'vars.JOB_IF == "true"',
|
|
649
|
+
success: 'vars.JOB_SUCCESS == "true"',
|
|
650
|
+
steps: [
|
|
651
|
+
{
|
|
652
|
+
if: expression('vars.STEP_IF == "true"'),
|
|
653
|
+
gate: {success: expression('vars.GATE_SUCCESS == "true"')},
|
|
654
|
+
run: 'echo build',
|
|
655
|
+
},
|
|
656
|
+
],
|
|
657
|
+
},
|
|
658
|
+
listen: {
|
|
659
|
+
listening: {
|
|
660
|
+
on: [{source: 'github', event: 'push', filter: 'vars.LISTENER_ON == "true"'}],
|
|
661
|
+
until: [
|
|
662
|
+
{source: 'github', event: 'pull_request', filter: 'vars.LISTENER_UNTIL == "true"'},
|
|
663
|
+
],
|
|
664
|
+
onResolve: 'finish',
|
|
665
|
+
},
|
|
666
|
+
steps: [{run: 'echo listen'}],
|
|
667
|
+
},
|
|
668
|
+
},
|
|
669
|
+
});
|
|
670
|
+
|
|
671
|
+
const run = await createWorkflowRun({
|
|
672
|
+
workspaceId,
|
|
673
|
+
projectId,
|
|
674
|
+
definitionId,
|
|
675
|
+
model,
|
|
676
|
+
triggerPayload: {
|
|
677
|
+
source: 'manual',
|
|
678
|
+
event: 'fire',
|
|
679
|
+
subscriptionId: crypto.randomUUID(),
|
|
680
|
+
userId: crypto.randomUUID(),
|
|
681
|
+
},
|
|
682
|
+
secrets,
|
|
683
|
+
});
|
|
684
|
+
|
|
685
|
+
const [attempt] = await listRunAttempts({workflowRunId: run.id, projectId});
|
|
686
|
+
|
|
687
|
+
expect(attempt?.vars).toEqual(values);
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
test('evaluates job and step predicates from the persisted vars snapshot', async () => {
|
|
691
|
+
const secrets = createTestSecretsClient();
|
|
692
|
+
await secrets.setSecrets({workspaceId, projectId, values: {ENABLED: 'true'}});
|
|
693
|
+
const model = workflowModel({
|
|
694
|
+
jobs: {
|
|
695
|
+
build: {
|
|
696
|
+
if: 'vars.ENABLED == "true"',
|
|
697
|
+
steps: [{if: expression('vars.ENABLED == "true"'), run: 'echo build'}],
|
|
698
|
+
},
|
|
699
|
+
},
|
|
700
|
+
});
|
|
701
|
+
|
|
702
|
+
const run = await createWorkflowRun({
|
|
703
|
+
workspaceId,
|
|
704
|
+
projectId,
|
|
705
|
+
definitionId,
|
|
706
|
+
model,
|
|
707
|
+
triggerPayload: {
|
|
708
|
+
source: 'manual',
|
|
709
|
+
event: 'fire',
|
|
710
|
+
subscriptionId: crypto.randomUUID(),
|
|
711
|
+
userId: crypto.randomUUID(),
|
|
712
|
+
},
|
|
713
|
+
secrets,
|
|
714
|
+
});
|
|
715
|
+
await secrets.setSecrets({workspaceId, projectId, values: {ENABLED: 'false'}});
|
|
716
|
+
|
|
717
|
+
const [job] = await getJobsByWorkflowRunId(run.id);
|
|
718
|
+
if (!job) throw new Error('Expected workflow job');
|
|
719
|
+
const activation = await evaluateJobActivations({
|
|
720
|
+
runAttemptId: job.workflowRunAttemptId,
|
|
721
|
+
jobs: [{jobId: job.id, expectedVersion: job.version}],
|
|
722
|
+
});
|
|
723
|
+
expect(activation).toEqual([{kind: 'start-job', jobId: job.id}]);
|
|
724
|
+
|
|
725
|
+
const [execution] = await getJobExecutionsByJobId(job.id);
|
|
726
|
+
if (!execution) throw new Error('Expected job execution');
|
|
727
|
+
const setup = await nextStepForJob(job.id);
|
|
728
|
+
if (setup.kind !== 'step') throw new Error('Expected setup step');
|
|
729
|
+
expect(setup).toMatchObject({kind: 'step', step: {type: 'setup'}});
|
|
730
|
+
await recordStepResult({
|
|
731
|
+
jobExecutionId: execution.id,
|
|
732
|
+
stepId: setup.step.id,
|
|
733
|
+
status: 'succeeded',
|
|
734
|
+
});
|
|
735
|
+
|
|
736
|
+
const guarded = await nextStepForJob(job.id);
|
|
737
|
+
expect(guarded).toMatchObject({kind: 'step', step: {position: 1}, dispatched: true});
|
|
738
|
+
});
|
|
739
|
+
|
|
269
740
|
test('returns the persisted model in run detail', async () => {
|
|
270
741
|
const model = buildModel({
|
|
271
742
|
env: {RUN_ID: template('run.id')},
|
|
@@ -335,7 +806,8 @@ describe('workflow run queries', () => {
|
|
|
335
806
|
runner: 'ubuntu-latest',
|
|
336
807
|
jobs: {
|
|
337
808
|
listen: {
|
|
338
|
-
name:
|
|
809
|
+
name: 'Process review',
|
|
810
|
+
executionName: displayNameSource,
|
|
339
811
|
listening: {
|
|
340
812
|
on: [{source: 'github', event: 'pull_request_review'}],
|
|
341
813
|
until: [{source: 'github', event: 'pull_request'}],
|
|
@@ -370,7 +842,7 @@ describe('workflow run queries', () => {
|
|
|
370
842
|
const build = runJobs.find((job) => job.key === 'build');
|
|
371
843
|
expect(listen).toMatchObject({
|
|
372
844
|
mode: 'listening',
|
|
373
|
-
name:
|
|
845
|
+
name: 'Process review',
|
|
374
846
|
listeningTimeoutMs: 30 * 24 * 60 * 60 * 1000,
|
|
375
847
|
maxExecutions: 3,
|
|
376
848
|
onResolve: 'cancel',
|
|
@@ -405,7 +877,8 @@ describe('workflow run queries', () => {
|
|
|
405
877
|
runner: 'ubuntu-latest',
|
|
406
878
|
jobs: {
|
|
407
879
|
listen: {
|
|
408
|
-
name:
|
|
880
|
+
name: 'Process review',
|
|
881
|
+
executionName: displayNameSource,
|
|
409
882
|
listening: {
|
|
410
883
|
on: [{source: 'github', event: 'pull_request_review'}],
|
|
411
884
|
until: [{source: 'github', event: 'pull_request'}],
|
|
@@ -561,6 +1034,46 @@ describe('workflow run queries', () => {
|
|
|
561
1034
|
}),
|
|
562
1035
|
expected: {field: 'step.name', source: 'vars.REQUIRED'},
|
|
563
1036
|
},
|
|
1037
|
+
{
|
|
1038
|
+
field: 'step.working_directory',
|
|
1039
|
+
model: () =>
|
|
1040
|
+
workflowModel({
|
|
1041
|
+
name: 'Missing working directory var',
|
|
1042
|
+
runner: 'ubuntu-latest',
|
|
1043
|
+
jobs: {
|
|
1044
|
+
build: {
|
|
1045
|
+
steps: [{workingDirectory: template('vars.REQUIRED'), run: 'echo ok'}],
|
|
1046
|
+
},
|
|
1047
|
+
},
|
|
1048
|
+
}),
|
|
1049
|
+
expected: {field: 'step.working_directory', source: 'vars.REQUIRED'},
|
|
1050
|
+
},
|
|
1051
|
+
{
|
|
1052
|
+
field: 'checkout.repository',
|
|
1053
|
+
model: () =>
|
|
1054
|
+
workflowModel({
|
|
1055
|
+
name: 'Missing checkout repository var',
|
|
1056
|
+
runner: 'ubuntu-latest',
|
|
1057
|
+
jobs: {
|
|
1058
|
+
build: {
|
|
1059
|
+
steps: [
|
|
1060
|
+
{
|
|
1061
|
+
checkout: {
|
|
1062
|
+
repository: template('vars.REQUIRED'),
|
|
1063
|
+
fetchDepth: 1,
|
|
1064
|
+
permissions: {contents: 'read'},
|
|
1065
|
+
persistCredentials: true,
|
|
1066
|
+
templates: {
|
|
1067
|
+
repository: checkoutTemplate(template('vars.REQUIRED')),
|
|
1068
|
+
},
|
|
1069
|
+
},
|
|
1070
|
+
},
|
|
1071
|
+
],
|
|
1072
|
+
},
|
|
1073
|
+
},
|
|
1074
|
+
}),
|
|
1075
|
+
expected: {field: 'checkout.repository', source: 'vars.REQUIRED'},
|
|
1076
|
+
},
|
|
564
1077
|
] as const)('reports missing variables against $field', async ({model, expected}) => {
|
|
565
1078
|
let error: unknown;
|
|
566
1079
|
try {
|
|
@@ -585,6 +1098,42 @@ describe('workflow run queries', () => {
|
|
|
585
1098
|
expect(error).toMatchObject(expected);
|
|
586
1099
|
});
|
|
587
1100
|
|
|
1101
|
+
test('resolves a working directory that references a workspace variable', async () => {
|
|
1102
|
+
const secrets = createTestSecretsClient();
|
|
1103
|
+
await secrets.setSecrets({
|
|
1104
|
+
workspaceId,
|
|
1105
|
+
namespace: '',
|
|
1106
|
+
values: {BUILD_DIR: 'packages/api'},
|
|
1107
|
+
});
|
|
1108
|
+
const model = workflowModel({
|
|
1109
|
+
name: 'Working directory from var',
|
|
1110
|
+
runner: 'ubuntu-latest',
|
|
1111
|
+
jobs: {
|
|
1112
|
+
build: {
|
|
1113
|
+
steps: [{workingDirectory: template('vars.BUILD_DIR'), run: 'echo ok'}],
|
|
1114
|
+
},
|
|
1115
|
+
},
|
|
1116
|
+
});
|
|
1117
|
+
|
|
1118
|
+
const run = await createWorkflowRun({
|
|
1119
|
+
workspaceId,
|
|
1120
|
+
projectId,
|
|
1121
|
+
definitionId,
|
|
1122
|
+
model,
|
|
1123
|
+
triggerPayload: {
|
|
1124
|
+
source: 'manual',
|
|
1125
|
+
event: 'fire',
|
|
1126
|
+
subscriptionId: crypto.randomUUID(),
|
|
1127
|
+
userId: crypto.randomUUID(),
|
|
1128
|
+
},
|
|
1129
|
+
secrets,
|
|
1130
|
+
});
|
|
1131
|
+
|
|
1132
|
+
const [build] = await getJobsByWorkflowRunId(run.id);
|
|
1133
|
+
const [, step] = await getStepsByJobId(build?.id as string);
|
|
1134
|
+
expect(step?.config).toMatchObject({run: 'echo ok', working_directory: 'packages/api'});
|
|
1135
|
+
});
|
|
1136
|
+
|
|
588
1137
|
test('writes workflows.workflow_run_attempt.created outbox event in same transaction', async () => {
|
|
589
1138
|
const run = await createWorkflowRun({
|
|
590
1139
|
workspaceId,
|
|
@@ -1186,6 +1735,7 @@ describe('workflow run queries', () => {
|
|
|
1186
1735
|
});
|
|
1187
1736
|
|
|
1188
1737
|
expect(second.id).toBe(first.id);
|
|
1738
|
+
expect(second.number).toBe(first.number);
|
|
1189
1739
|
expect(second.triggerIdempotencyKey).toBe(idempotencyKey);
|
|
1190
1740
|
expect(second.sourceSnapshot).toEqual({
|
|
1191
1741
|
content: 'name: Original\njobs: {}\n',
|
|
@@ -1202,6 +1752,54 @@ describe('workflow run queries', () => {
|
|
|
1202
1752
|
.from(workflowsOutbox)
|
|
1203
1753
|
.where(sql`${workflowsOutbox.payload}->>'workflowRunId' = ${first.id}`);
|
|
1204
1754
|
expect(outboxRows).toHaveLength(1);
|
|
1755
|
+
const [counter] = await db()
|
|
1756
|
+
.select()
|
|
1757
|
+
.from(workflowRunCounters)
|
|
1758
|
+
.where(eq(workflowRunCounters.definitionId, definitionId));
|
|
1759
|
+
expect(counter?.nextNumber).toBe(2);
|
|
1760
|
+
});
|
|
1761
|
+
|
|
1762
|
+
test('duplicate triggerIdempotencyKey returns the persisted run name without reevaluating it', async () => {
|
|
1763
|
+
const idempotencyKey = crypto.randomUUID();
|
|
1764
|
+
const first = await createWorkflowRun({
|
|
1765
|
+
workspaceId,
|
|
1766
|
+
projectId,
|
|
1767
|
+
definitionId,
|
|
1768
|
+
model: buildModel({
|
|
1769
|
+
name: 'Deploy application',
|
|
1770
|
+
runName: `Deploy ${template('event.environment')}`,
|
|
1771
|
+
}),
|
|
1772
|
+
triggerPayload: {
|
|
1773
|
+
source: 'github',
|
|
1774
|
+
event: 'deployment',
|
|
1775
|
+
deliveryId: 'delivery-1',
|
|
1776
|
+
data: {environment: 'production'},
|
|
1777
|
+
},
|
|
1778
|
+
triggerIdempotencyKey: idempotencyKey,
|
|
1779
|
+
});
|
|
1780
|
+
const replay = await createWorkflowRun({
|
|
1781
|
+
workspaceId,
|
|
1782
|
+
projectId,
|
|
1783
|
+
definitionId,
|
|
1784
|
+
model: buildModel({
|
|
1785
|
+
name: 'Changed workflow',
|
|
1786
|
+
runName: `Changed ${template('event.environment')}`,
|
|
1787
|
+
}),
|
|
1788
|
+
triggerPayload: {
|
|
1789
|
+
source: 'github',
|
|
1790
|
+
event: 'deployment',
|
|
1791
|
+
deliveryId: 'delivery-2',
|
|
1792
|
+
data: {environment: 'staging'},
|
|
1793
|
+
},
|
|
1794
|
+
triggerIdempotencyKey: idempotencyKey,
|
|
1795
|
+
});
|
|
1796
|
+
|
|
1797
|
+
expect(replay).toMatchObject({
|
|
1798
|
+
id: first.id,
|
|
1799
|
+
name: 'Deploy production',
|
|
1800
|
+
workflowName: 'Deploy application',
|
|
1801
|
+
nameOverride: 'Deploy production',
|
|
1802
|
+
});
|
|
1205
1803
|
});
|
|
1206
1804
|
|
|
1207
1805
|
test('duplicate triggerIdempotencyKey returns the existing run without re-materializing', async () => {
|