@shipfox/api-runners 12.7.0 → 13.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.
Files changed (55) hide show
  1. package/.turbo/turbo-build.log +4 -4
  2. package/CHANGELOG.md +12 -0
  3. package/dist/db/index.d.ts +1 -1
  4. package/dist/db/index.d.ts.map +1 -1
  5. package/dist/db/index.js +1 -1
  6. package/dist/db/index.js.map +1 -1
  7. package/dist/db/job-executions.d.ts +8 -24
  8. package/dist/db/job-executions.d.ts.map +1 -1
  9. package/dist/db/job-executions.js +62 -81
  10. package/dist/db/job-executions.js.map +1 -1
  11. package/dist/db/reservations.d.ts.map +1 -1
  12. package/dist/db/reservations.js +6 -4
  13. package/dist/db/reservations.js.map +1 -1
  14. package/dist/index.d.ts +1 -1
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +5 -4
  17. package/dist/index.js.map +1 -1
  18. package/dist/presentation/index.d.ts +2 -1
  19. package/dist/presentation/index.d.ts.map +1 -1
  20. package/dist/presentation/index.js +2 -1
  21. package/dist/presentation/index.js.map +1 -1
  22. package/dist/presentation/inter-module.d.ts +0 -1
  23. package/dist/presentation/inter-module.d.ts.map +1 -1
  24. package/dist/presentation/inter-module.js +2 -25
  25. package/dist/presentation/inter-module.js.map +1 -1
  26. package/dist/presentation/subscribers/on-workflows-job-execution-queued.d.ts +3 -0
  27. package/dist/presentation/subscribers/on-workflows-job-execution-queued.d.ts.map +1 -0
  28. package/dist/presentation/subscribers/on-workflows-job-execution-queued.js +21 -0
  29. package/dist/presentation/subscribers/on-workflows-job-execution-queued.js.map +1 -0
  30. package/dist/presentation/subscribers/on-workflows-job-execution-terminated.d.ts +3 -0
  31. package/dist/presentation/subscribers/on-workflows-job-execution-terminated.d.ts.map +1 -0
  32. package/dist/presentation/subscribers/{on-workflows-job-execution-timed-out.js → on-workflows-job-execution-terminated.js} +5 -4
  33. package/dist/presentation/subscribers/on-workflows-job-execution-terminated.js.map +1 -0
  34. package/dist/tsconfig.test.tsbuildinfo +1 -1
  35. package/package.json +3 -3
  36. package/src/db/index.ts +0 -2
  37. package/src/db/job-executions.test.ts +105 -335
  38. package/src/db/job-executions.ts +70 -91
  39. package/src/db/reservations.ts +6 -3
  40. package/src/db/runner-instances.test.ts +100 -0
  41. package/src/index.test.ts +1 -0
  42. package/src/index.ts +8 -7
  43. package/src/presentation/index.ts +2 -1
  44. package/src/presentation/inter-module.ts +2 -40
  45. package/src/presentation/subscribers/on-workflows-job-execution-queued.test.ts +57 -0
  46. package/src/presentation/subscribers/on-workflows-job-execution-queued.ts +26 -0
  47. package/src/presentation/subscribers/on-workflows-job-execution-terminated.test.ts +142 -0
  48. package/src/presentation/subscribers/{on-workflows-job-execution-timed-out.ts → on-workflows-job-execution-terminated.ts} +5 -4
  49. package/test/factories/pending-job.ts +3 -0
  50. package/tsconfig.build.tsbuildinfo +1 -1
  51. package/dist/presentation/subscribers/on-workflows-job-execution-timed-out.d.ts +0 -3
  52. package/dist/presentation/subscribers/on-workflows-job-execution-timed-out.d.ts.map +0 -1
  53. package/dist/presentation/subscribers/on-workflows-job-execution-timed-out.js.map +0 -1
  54. package/src/presentation/inter-module.test.ts +0 -15
  55. package/src/presentation/subscribers/on-workflows-job-execution-timed-out.test.ts +0 -163
@@ -1,3 +0,0 @@
1
- import type { WorkflowsJobExecutionTimedOutEventDto } from '@shipfox/api-workflows-dto';
2
- export declare function onWorkflowsJobExecutionTimedOut(payload: WorkflowsJobExecutionTimedOutEventDto): Promise<void>;
3
- //# sourceMappingURL=on-workflows-job-execution-timed-out.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"on-workflows-job-execution-timed-out.d.ts","sourceRoot":"","sources":["../../../src/presentation/subscribers/on-workflows-job-execution-timed-out.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,qCAAqC,EAAC,MAAM,4BAA4B,CAAC;AAItF,wBAAsB,+BAA+B,CACnD,OAAO,EAAE,qCAAqC,GAC7C,OAAO,CAAC,IAAI,CAAC,CAUf"}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../src/presentation/subscribers/on-workflows-job-execution-timed-out.ts"],"sourcesContent":["import type {WorkflowsJobExecutionTimedOutEventDto} from '@shipfox/api-workflows-dto';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport {reconcileTerminalJobExecution} from '#db/job-executions.js';\n\nexport async function onWorkflowsJobExecutionTimedOut(\n payload: WorkflowsJobExecutionTimedOutEventDto,\n): Promise<void> {\n logger().info(\n {\n jobId: payload.jobId,\n jobExecutionId: payload.jobExecutionId,\n workflowRunAttemptId: payload.workflowRunAttemptId,\n },\n 'Reconciling runner state for timed-out job execution',\n );\n await reconcileTerminalJobExecution({jobExecutionId: payload.jobExecutionId});\n}\n"],"names":["logger","reconcileTerminalJobExecution","onWorkflowsJobExecutionTimedOut","payload","info","jobId","jobExecutionId","workflowRunAttemptId"],"mappings":"AACA,SAAQA,MAAM,QAAO,8BAA8B;AACnD,SAAQC,6BAA6B,QAAO,wBAAwB;AAEpE,OAAO,eAAeC,gCACpBC,OAA8C;IAE9CH,SAASI,IAAI,CACX;QACEC,OAAOF,QAAQE,KAAK;QACpBC,gBAAgBH,QAAQG,cAAc;QACtCC,sBAAsBJ,QAAQI,oBAAoB;IACpD,GACA;IAEF,MAAMN,8BAA8B;QAACK,gBAAgBH,QAAQG,cAAc;IAAA;AAC7E"}
@@ -1,15 +0,0 @@
1
- import {runnersInterModuleContract} from '@shipfox/api-runners-dto/inter-module';
2
- import {isInterModuleKnownError} from '@shipfox/inter-module';
3
- import {EmptyRequiredLabelsError} from '#core/errors.js';
4
- import {toEnqueueJobExecutionKnownError} from './inter-module.js';
5
-
6
- describe('Runners inter-module presentation', () => {
7
- test('maps empty scheduling labels to the published contract error', () => {
8
- const result = toEnqueueJobExecutionKnownError(new EmptyRequiredLabelsError());
9
-
10
- expect(
11
- isInterModuleKnownError(runnersInterModuleContract.methods.enqueueJobExecution, result) &&
12
- result.code,
13
- ).toBe('empty-required-labels');
14
- });
15
- });
@@ -1,163 +0,0 @@
1
- import type {WorkflowsJobExecutionTimedOutEventDto} from '@shipfox/api-workflows-dto';
2
- import {eq} from 'drizzle-orm';
3
- import {db} from '#db/db.js';
4
- import {claimPendingJobExecution} from '#db/job-executions.js';
5
- import {pendingJobExecutions} from '#db/schema/pending-job-executions.js';
6
- import {runningJobExecutions} from '#db/schema/running-job-executions.js';
7
- import {pendingJobFactory, runnerSessionFactory} from '#test/index.js';
8
- import {onWorkflowsJobExecutionTimedOut} from './on-workflows-job-execution-timed-out.js';
9
-
10
- function buildPayload(
11
- jobId: string,
12
- jobExecutionId: string,
13
- workflowRunAttemptId: string,
14
- ): WorkflowsJobExecutionTimedOutEventDto {
15
- return {jobId, jobExecutionId, workflowRunAttemptId};
16
- }
17
-
18
- describe('onWorkflowsJobExecutionTimedOut', () => {
19
- let workspaceId: string;
20
- let runnerSessionId: string;
21
-
22
- beforeEach(async () => {
23
- workspaceId = crypto.randomUUID();
24
- const runnerSession = await runnerSessionFactory.create({workspaceId});
25
- runnerSessionId = runnerSession.id;
26
- });
27
-
28
- it('deletes an unclaimed pending execution', async () => {
29
- const pending = await pendingJobFactory.create({workspaceId});
30
-
31
- await onWorkflowsJobExecutionTimedOut(
32
- buildPayload(pending.jobId, pending.jobExecutionId, pending.workflowRunAttemptId),
33
- );
34
-
35
- expect(
36
- await db()
37
- .select()
38
- .from(pendingJobExecutions)
39
- .where(eq(pendingJobExecutions.jobExecutionId, pending.jobExecutionId)),
40
- ).toHaveLength(0);
41
- });
42
-
43
- it('sets cancellation_requested_at on the matching running_jobs row', async () => {
44
- await pendingJobFactory.create({workspaceId});
45
- const claimed = await claimPendingJobExecution({
46
- workspaceId,
47
- runnerSessionId,
48
- sessionLabels: ['linux', 'x64'],
49
- maxClaims: null,
50
- runnerSessionLivenessThrottleSeconds: 10,
51
- });
52
- expect(claimed).not.toBeNull();
53
-
54
- await onWorkflowsJobExecutionTimedOut(
55
- buildPayload(
56
- claimed?.jobId as string,
57
- claimed?.jobExecutionId as string,
58
- claimed?.workflowRunAttemptId as string,
59
- ),
60
- );
61
-
62
- const rows = await db()
63
- .select()
64
- .from(runningJobExecutions)
65
- .where(eq(runningJobExecutions.jobId, claimed?.jobId as string));
66
- expect(rows[0]?.cancellationRequestedAt).not.toBeNull();
67
- });
68
-
69
- it('does not cancel another running execution for the same job', async () => {
70
- await pendingJobFactory.create({workspaceId});
71
- const claimed = await claimPendingJobExecution({
72
- workspaceId,
73
- runnerSessionId,
74
- sessionLabels: ['linux', 'x64'],
75
- maxClaims: null,
76
- runnerSessionLivenessThrottleSeconds: 10,
77
- });
78
- expect(claimed).not.toBeNull();
79
- const siblingJobExecutionId = crypto.randomUUID();
80
- const siblingRunnerSession = await runnerSessionFactory.create({workspaceId});
81
- await db()
82
- .insert(runningJobExecutions)
83
- .values({
84
- workspaceId,
85
- workflowRunId: claimed?.workflowRunId as string,
86
- jobId: claimed?.jobId as string,
87
- jobExecutionId: siblingJobExecutionId,
88
- workflowRunAttemptId: claimed?.workflowRunAttemptId as string,
89
- projectId: claimed?.projectId as string,
90
- runnerSessionId: siblingRunnerSession.id,
91
- requiredLabels: ['linux'],
92
- runnerLabels: ['linux', 'x64'],
93
- });
94
-
95
- await onWorkflowsJobExecutionTimedOut(
96
- buildPayload(
97
- claimed?.jobId as string,
98
- claimed?.jobExecutionId as string,
99
- claimed?.workflowRunAttemptId as string,
100
- ),
101
- );
102
-
103
- const rows = await db()
104
- .select({
105
- jobExecutionId: runningJobExecutions.jobExecutionId,
106
- cancellationRequestedAt: runningJobExecutions.cancellationRequestedAt,
107
- })
108
- .from(runningJobExecutions)
109
- .where(eq(runningJobExecutions.jobId, claimed?.jobId as string));
110
- const byJobExecutionId = new Map(
111
- rows.map((row) => [row.jobExecutionId, row.cancellationRequestedAt]),
112
- );
113
- expect(byJobExecutionId.get(claimed?.jobExecutionId as string)).not.toBeNull();
114
- expect(byJobExecutionId.get(siblingJobExecutionId)).toBeNull();
115
- });
116
-
117
- it('idempotent under double delivery: second call preserves the first timestamp', async () => {
118
- await pendingJobFactory.create({workspaceId});
119
- const claimed = await claimPendingJobExecution({
120
- workspaceId,
121
- runnerSessionId,
122
- sessionLabels: ['linux', 'x64'],
123
- maxClaims: null,
124
- runnerSessionLivenessThrottleSeconds: 10,
125
- });
126
-
127
- await onWorkflowsJobExecutionTimedOut(
128
- buildPayload(
129
- claimed?.jobId as string,
130
- claimed?.jobExecutionId as string,
131
- claimed?.workflowRunAttemptId as string,
132
- ),
133
- );
134
- const after1 = await db()
135
- .select()
136
- .from(runningJobExecutions)
137
- .where(eq(runningJobExecutions.jobId, claimed?.jobId as string));
138
- const firstTs = after1[0]?.cancellationRequestedAt;
139
-
140
- await new Promise((r) => setTimeout(r, 10));
141
- await onWorkflowsJobExecutionTimedOut(
142
- buildPayload(
143
- claimed?.jobId as string,
144
- claimed?.jobExecutionId as string,
145
- claimed?.workflowRunAttemptId as string,
146
- ),
147
- );
148
-
149
- const after2 = await db()
150
- .select()
151
- .from(runningJobExecutions)
152
- .where(eq(runningJobExecutions.jobId, claimed?.jobId as string));
153
- expect(after2[0]?.cancellationRequestedAt?.getTime()).toBe(firstTs?.getTime());
154
- });
155
-
156
- it('no-op when the running_jobs row is gone (already finalized)', async () => {
157
- await expect(
158
- onWorkflowsJobExecutionTimedOut(
159
- buildPayload(crypto.randomUUID(), crypto.randomUUID(), crypto.randomUUID()),
160
- ),
161
- ).resolves.toBeUndefined();
162
- });
163
- });