@norskvideo/ctl-dev-kit 0.2.22 → 0.2.23
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/conventions/build-docs.yml +18 -0
- package/conventions/build-image.yml +72 -0
- package/conventions/checks.sdk-app.yml +54 -0
- package/conventions/checks.yml +54 -0
- package/conventions/integration.yml +36 -0
- package/conventions/publish-docs.yml +18 -0
- package/conventions/smoke.yml +36 -0
- package/conventions/sync-ctl-packages.yml +33 -3
- package/conventions/sync-dev-kit.yml +49 -2
- package/conventions/upgrade-latest.yml +36 -0
- package/conventions/workflow-shell.ts +94 -0
- package/package.json +1 -1
|
@@ -244,3 +244,21 @@ jobs:
|
|
|
244
244
|
"
|
|
245
245
|
docker image rm -f "$IMAGE_TAG" >/dev/null
|
|
246
246
|
'
|
|
247
|
+
- name: Reap the containers this job launched
|
|
248
|
+
# Whatever the tests did. A teardown cut short by a test timeout leaves
|
|
249
|
+
# its instance running on the shared Docker daemon (see JOB_REAP_STEP in
|
|
250
|
+
# conventions/workflow-shell.ts); everything this job launched mounts its
|
|
251
|
+
# own test-temp, so that is what goes.
|
|
252
|
+
if: always()
|
|
253
|
+
run: |
|
|
254
|
+
base="${NORSK_CTL_TEST_TMP:-}"
|
|
255
|
+
tt="$GITHUB_WORKSPACE/test-temp"
|
|
256
|
+
s=""
|
|
257
|
+
for c in $(docker ps -aq); do
|
|
258
|
+
src="$(docker inspect -f '{{range .Mounts}}{{println .Source}}{{end}}' "$c" 2>/dev/null)" || continue
|
|
259
|
+
if { [ -n "$base" ] && grep -q "^$base/" <<<"$src"; } || grep -q "^$tt/" <<<"$src"; then s="$s $c"; fi
|
|
260
|
+
done
|
|
261
|
+
if [ -n "$s" ]; then
|
|
262
|
+
echo "::warning::removing containers this job left running:$s"
|
|
263
|
+
docker rm -f $s
|
|
264
|
+
fi
|
|
@@ -134,6 +134,24 @@ jobs:
|
|
|
134
134
|
echo "channel=${channel}" >> "$GITHUB_OUTPUT"
|
|
135
135
|
echo "publish=${publish}" >> "$GITHUB_OUTPUT"
|
|
136
136
|
echo "label=${label} channel=${channel} publish=${publish}"
|
|
137
|
+
- name: Reap the containers this job launched
|
|
138
|
+
# Whatever the tests did. A teardown cut short by a test timeout leaves
|
|
139
|
+
# its instance running on the shared Docker daemon (see JOB_REAP_STEP in
|
|
140
|
+
# conventions/workflow-shell.ts); everything this job launched mounts its
|
|
141
|
+
# own test-temp, so that is what goes.
|
|
142
|
+
if: always()
|
|
143
|
+
run: |
|
|
144
|
+
base="${NORSK_CTL_TEST_TMP:-}"
|
|
145
|
+
tt="$GITHUB_WORKSPACE/test-temp"
|
|
146
|
+
s=""
|
|
147
|
+
for c in $(docker ps -aq); do
|
|
148
|
+
src="$(docker inspect -f '{{range .Mounts}}{{println .Source}}{{end}}' "$c" 2>/dev/null)" || continue
|
|
149
|
+
if { [ -n "$base" ] && grep -q "^$base/" <<<"$src"; } || grep -q "^$tt/" <<<"$src"; then s="$s $c"; fi
|
|
150
|
+
done
|
|
151
|
+
if [ -n "$s" ]; then
|
|
152
|
+
echo "::warning::removing containers this job left running:$s"
|
|
153
|
+
docker rm -f $s
|
|
154
|
+
fi
|
|
137
155
|
|
|
138
156
|
build:
|
|
139
157
|
needs: label
|
|
@@ -232,6 +250,24 @@ jobs:
|
|
|
232
250
|
- name: Push the per-arch image to Docker Hub
|
|
233
251
|
if: ${{ env.PUBLISH_IMAGE != '' }}
|
|
234
252
|
run: docker push "$IMAGE_TAG"
|
|
253
|
+
- name: Reap the containers this job launched
|
|
254
|
+
# Whatever the tests did. A teardown cut short by a test timeout leaves
|
|
255
|
+
# its instance running on the shared Docker daemon (see JOB_REAP_STEP in
|
|
256
|
+
# conventions/workflow-shell.ts); everything this job launched mounts its
|
|
257
|
+
# own test-temp, so that is what goes.
|
|
258
|
+
if: always()
|
|
259
|
+
run: |
|
|
260
|
+
base="${NORSK_CTL_TEST_TMP:-}"
|
|
261
|
+
tt="$GITHUB_WORKSPACE/test-temp"
|
|
262
|
+
s=""
|
|
263
|
+
for c in $(docker ps -aq); do
|
|
264
|
+
src="$(docker inspect -f '{{range .Mounts}}{{println .Source}}{{end}}' "$c" 2>/dev/null)" || continue
|
|
265
|
+
if { [ -n "$base" ] && grep -q "^$base/" <<<"$src"; } || grep -q "^$tt/" <<<"$src"; then s="$s $c"; fi
|
|
266
|
+
done
|
|
267
|
+
if [ -n "$s" ]; then
|
|
268
|
+
echo "::warning::removing containers this job left running:$s"
|
|
269
|
+
docker rm -f $s
|
|
270
|
+
fi
|
|
235
271
|
|
|
236
272
|
# Combine the per-arch images into a multi-arch manifest and write the two
|
|
237
273
|
# published tags. Skipped for a build-only product (empty PUBLISH_IMAGE) and on
|
|
@@ -320,6 +356,24 @@ jobs:
|
|
|
320
356
|
-t "${PUBLISH_IMAGE}:${channel}" \
|
|
321
357
|
"${PUBLISH_IMAGE}:${label}-amd64" \
|
|
322
358
|
"${PUBLISH_IMAGE}:${label}-arm64"
|
|
359
|
+
- name: Reap the containers this job launched
|
|
360
|
+
# Whatever the tests did. A teardown cut short by a test timeout leaves
|
|
361
|
+
# its instance running on the shared Docker daemon (see JOB_REAP_STEP in
|
|
362
|
+
# conventions/workflow-shell.ts); everything this job launched mounts its
|
|
363
|
+
# own test-temp, so that is what goes.
|
|
364
|
+
if: always()
|
|
365
|
+
run: |
|
|
366
|
+
base="${NORSK_CTL_TEST_TMP:-}"
|
|
367
|
+
tt="$GITHUB_WORKSPACE/test-temp"
|
|
368
|
+
s=""
|
|
369
|
+
for c in $(docker ps -aq); do
|
|
370
|
+
src="$(docker inspect -f '{{range .Mounts}}{{println .Source}}{{end}}' "$c" 2>/dev/null)" || continue
|
|
371
|
+
if { [ -n "$base" ] && grep -q "^$base/" <<<"$src"; } || grep -q "^$tt/" <<<"$src"; then s="$s $c"; fi
|
|
372
|
+
done
|
|
373
|
+
if [ -n "$s" ]; then
|
|
374
|
+
echo "::warning::removing containers this job left running:$s"
|
|
375
|
+
docker rm -f $s
|
|
376
|
+
fi
|
|
323
377
|
|
|
324
378
|
# Report this pipeline's result to the aggregated product CI dashboard
|
|
325
379
|
# (id3as/ci-workflows) instead of posting its own pony -- the dashboard renders
|
|
@@ -402,3 +456,21 @@ jobs:
|
|
|
402
456
|
product: __PRODUCT__
|
|
403
457
|
pipeline: build-image
|
|
404
458
|
status: ${{ steps.meta.outputs.state }}
|
|
459
|
+
- name: Reap the containers this job launched
|
|
460
|
+
# Whatever the tests did. A teardown cut short by a test timeout leaves
|
|
461
|
+
# its instance running on the shared Docker daemon (see JOB_REAP_STEP in
|
|
462
|
+
# conventions/workflow-shell.ts); everything this job launched mounts its
|
|
463
|
+
# own test-temp, so that is what goes.
|
|
464
|
+
if: always()
|
|
465
|
+
run: |
|
|
466
|
+
base="${NORSK_CTL_TEST_TMP:-}"
|
|
467
|
+
tt="$GITHUB_WORKSPACE/test-temp"
|
|
468
|
+
s=""
|
|
469
|
+
for c in $(docker ps -aq); do
|
|
470
|
+
src="$(docker inspect -f '{{range .Mounts}}{{println .Source}}{{end}}' "$c" 2>/dev/null)" || continue
|
|
471
|
+
if { [ -n "$base" ] && grep -q "^$base/" <<<"$src"; } || grep -q "^$tt/" <<<"$src"; then s="$s $c"; fi
|
|
472
|
+
done
|
|
473
|
+
if [ -n "$s" ]; then
|
|
474
|
+
echo "::warning::removing containers this job left running:$s"
|
|
475
|
+
docker rm -f $s
|
|
476
|
+
fi
|
|
@@ -106,6 +106,24 @@ jobs:
|
|
|
106
106
|
bun install --frozen-lockfile
|
|
107
107
|
bun run check:drift
|
|
108
108
|
'
|
|
109
|
+
- name: Reap the containers this job launched
|
|
110
|
+
# Whatever the tests did. A teardown cut short by a test timeout leaves
|
|
111
|
+
# its instance running on the shared Docker daemon (see JOB_REAP_STEP in
|
|
112
|
+
# conventions/workflow-shell.ts); everything this job launched mounts its
|
|
113
|
+
# own test-temp, so that is what goes.
|
|
114
|
+
if: always()
|
|
115
|
+
run: |
|
|
116
|
+
base="${NORSK_CTL_TEST_TMP:-}"
|
|
117
|
+
tt="$GITHUB_WORKSPACE/test-temp"
|
|
118
|
+
s=""
|
|
119
|
+
for c in $(docker ps -aq); do
|
|
120
|
+
src="$(docker inspect -f '{{range .Mounts}}{{println .Source}}{{end}}' "$c" 2>/dev/null)" || continue
|
|
121
|
+
if { [ -n "$base" ] && grep -q "^$base/" <<<"$src"; } || grep -q "^$tt/" <<<"$src"; then s="$s $c"; fi
|
|
122
|
+
done
|
|
123
|
+
if [ -n "$s" ]; then
|
|
124
|
+
echo "::warning::removing containers this job left running:$s"
|
|
125
|
+
docker rm -f $s
|
|
126
|
+
fi
|
|
109
127
|
|
|
110
128
|
quality:
|
|
111
129
|
runs-on: x64
|
|
@@ -190,6 +208,24 @@ jobs:
|
|
|
190
208
|
# in here and breaks a schema handed across the instance boundary.
|
|
191
209
|
bun node_modules/@norskvideo/ctl-dev-kit/conventions/zod-singleton.ts
|
|
192
210
|
'
|
|
211
|
+
- name: Reap the containers this job launched
|
|
212
|
+
# Whatever the tests did. A teardown cut short by a test timeout leaves
|
|
213
|
+
# its instance running on the shared Docker daemon (see JOB_REAP_STEP in
|
|
214
|
+
# conventions/workflow-shell.ts); everything this job launched mounts its
|
|
215
|
+
# own test-temp, so that is what goes.
|
|
216
|
+
if: always()
|
|
217
|
+
run: |
|
|
218
|
+
base="${NORSK_CTL_TEST_TMP:-}"
|
|
219
|
+
tt="$GITHUB_WORKSPACE/test-temp"
|
|
220
|
+
s=""
|
|
221
|
+
for c in $(docker ps -aq); do
|
|
222
|
+
src="$(docker inspect -f '{{range .Mounts}}{{println .Source}}{{end}}' "$c" 2>/dev/null)" || continue
|
|
223
|
+
if { [ -n "$base" ] && grep -q "^$base/" <<<"$src"; } || grep -q "^$tt/" <<<"$src"; then s="$s $c"; fi
|
|
224
|
+
done
|
|
225
|
+
if [ -n "$s" ]; then
|
|
226
|
+
echo "::warning::removing containers this job left running:$s"
|
|
227
|
+
docker rm -f $s
|
|
228
|
+
fi
|
|
193
229
|
|
|
194
230
|
# Report this pipeline's result to the aggregated product CI dashboard
|
|
195
231
|
# (id3as/ci-workflows) instead of posting its own pony — the dashboard renders
|
|
@@ -274,3 +310,21 @@ jobs:
|
|
|
274
310
|
product: __PRODUCT__
|
|
275
311
|
pipeline: checks
|
|
276
312
|
status: ${{ steps.meta.outputs.state }}
|
|
313
|
+
- name: Reap the containers this job launched
|
|
314
|
+
# Whatever the tests did. A teardown cut short by a test timeout leaves
|
|
315
|
+
# its instance running on the shared Docker daemon (see JOB_REAP_STEP in
|
|
316
|
+
# conventions/workflow-shell.ts); everything this job launched mounts its
|
|
317
|
+
# own test-temp, so that is what goes.
|
|
318
|
+
if: always()
|
|
319
|
+
run: |
|
|
320
|
+
base="${NORSK_CTL_TEST_TMP:-}"
|
|
321
|
+
tt="$GITHUB_WORKSPACE/test-temp"
|
|
322
|
+
s=""
|
|
323
|
+
for c in $(docker ps -aq); do
|
|
324
|
+
src="$(docker inspect -f '{{range .Mounts}}{{println .Source}}{{end}}' "$c" 2>/dev/null)" || continue
|
|
325
|
+
if { [ -n "$base" ] && grep -q "^$base/" <<<"$src"; } || grep -q "^$tt/" <<<"$src"; then s="$s $c"; fi
|
|
326
|
+
done
|
|
327
|
+
if [ -n "$s" ]; then
|
|
328
|
+
echo "::warning::removing containers this job left running:$s"
|
|
329
|
+
docker rm -f $s
|
|
330
|
+
fi
|
package/conventions/checks.yml
CHANGED
|
@@ -102,6 +102,24 @@ jobs:
|
|
|
102
102
|
bun install --frozen-lockfile
|
|
103
103
|
bun run check:drift
|
|
104
104
|
'
|
|
105
|
+
- name: Reap the containers this job launched
|
|
106
|
+
# Whatever the tests did. A teardown cut short by a test timeout leaves
|
|
107
|
+
# its instance running on the shared Docker daemon (see JOB_REAP_STEP in
|
|
108
|
+
# conventions/workflow-shell.ts); everything this job launched mounts its
|
|
109
|
+
# own test-temp, so that is what goes.
|
|
110
|
+
if: always()
|
|
111
|
+
run: |
|
|
112
|
+
base="${NORSK_CTL_TEST_TMP:-}"
|
|
113
|
+
tt="$GITHUB_WORKSPACE/test-temp"
|
|
114
|
+
s=""
|
|
115
|
+
for c in $(docker ps -aq); do
|
|
116
|
+
src="$(docker inspect -f '{{range .Mounts}}{{println .Source}}{{end}}' "$c" 2>/dev/null)" || continue
|
|
117
|
+
if { [ -n "$base" ] && grep -q "^$base/" <<<"$src"; } || grep -q "^$tt/" <<<"$src"; then s="$s $c"; fi
|
|
118
|
+
done
|
|
119
|
+
if [ -n "$s" ]; then
|
|
120
|
+
echo "::warning::removing containers this job left running:$s"
|
|
121
|
+
docker rm -f $s
|
|
122
|
+
fi
|
|
105
123
|
|
|
106
124
|
quality:
|
|
107
125
|
runs-on: x64
|
|
@@ -229,6 +247,24 @@ jobs:
|
|
|
229
247
|
nix develop .#build --command bun \
|
|
230
248
|
node_modules/@norskvideo/ctl-dev-kit/licence/check-product-name.ts \
|
|
231
249
|
shared/src/product-template.ts "$product"
|
|
250
|
+
- name: Reap the containers this job launched
|
|
251
|
+
# Whatever the tests did. A teardown cut short by a test timeout leaves
|
|
252
|
+
# its instance running on the shared Docker daemon (see JOB_REAP_STEP in
|
|
253
|
+
# conventions/workflow-shell.ts); everything this job launched mounts its
|
|
254
|
+
# own test-temp, so that is what goes.
|
|
255
|
+
if: always()
|
|
256
|
+
run: |
|
|
257
|
+
base="${NORSK_CTL_TEST_TMP:-}"
|
|
258
|
+
tt="$GITHUB_WORKSPACE/test-temp"
|
|
259
|
+
s=""
|
|
260
|
+
for c in $(docker ps -aq); do
|
|
261
|
+
src="$(docker inspect -f '{{range .Mounts}}{{println .Source}}{{end}}' "$c" 2>/dev/null)" || continue
|
|
262
|
+
if { [ -n "$base" ] && grep -q "^$base/" <<<"$src"; } || grep -q "^$tt/" <<<"$src"; then s="$s $c"; fi
|
|
263
|
+
done
|
|
264
|
+
if [ -n "$s" ]; then
|
|
265
|
+
echo "::warning::removing containers this job left running:$s"
|
|
266
|
+
docker rm -f $s
|
|
267
|
+
fi
|
|
232
268
|
|
|
233
269
|
# Report this pipeline's result to the aggregated product CI dashboard
|
|
234
270
|
# (id3as/ci-workflows) instead of posting its own pony — the dashboard renders
|
|
@@ -313,3 +349,21 @@ jobs:
|
|
|
313
349
|
product: __PRODUCT__
|
|
314
350
|
pipeline: checks
|
|
315
351
|
status: ${{ steps.meta.outputs.state }}
|
|
352
|
+
- name: Reap the containers this job launched
|
|
353
|
+
# Whatever the tests did. A teardown cut short by a test timeout leaves
|
|
354
|
+
# its instance running on the shared Docker daemon (see JOB_REAP_STEP in
|
|
355
|
+
# conventions/workflow-shell.ts); everything this job launched mounts its
|
|
356
|
+
# own test-temp, so that is what goes.
|
|
357
|
+
if: always()
|
|
358
|
+
run: |
|
|
359
|
+
base="${NORSK_CTL_TEST_TMP:-}"
|
|
360
|
+
tt="$GITHUB_WORKSPACE/test-temp"
|
|
361
|
+
s=""
|
|
362
|
+
for c in $(docker ps -aq); do
|
|
363
|
+
src="$(docker inspect -f '{{range .Mounts}}{{println .Source}}{{end}}' "$c" 2>/dev/null)" || continue
|
|
364
|
+
if { [ -n "$base" ] && grep -q "^$base/" <<<"$src"; } || grep -q "^$tt/" <<<"$src"; then s="$s $c"; fi
|
|
365
|
+
done
|
|
366
|
+
if [ -n "$s" ]; then
|
|
367
|
+
echo "::warning::removing containers this job left running:$s"
|
|
368
|
+
docker rm -f $s
|
|
369
|
+
fi
|
|
@@ -387,6 +387,24 @@ jobs:
|
|
|
387
387
|
fi
|
|
388
388
|
done
|
|
389
389
|
rm -f "$m" || true
|
|
390
|
+
- name: Reap the containers this job launched
|
|
391
|
+
# Whatever the tests did. A teardown cut short by a test timeout leaves
|
|
392
|
+
# its instance running on the shared Docker daemon (see JOB_REAP_STEP in
|
|
393
|
+
# conventions/workflow-shell.ts); everything this job launched mounts its
|
|
394
|
+
# own test-temp, so that is what goes.
|
|
395
|
+
if: always()
|
|
396
|
+
run: |
|
|
397
|
+
base="${NORSK_CTL_TEST_TMP:-}"
|
|
398
|
+
tt="$GITHUB_WORKSPACE/test-temp"
|
|
399
|
+
s=""
|
|
400
|
+
for c in $(docker ps -aq); do
|
|
401
|
+
src="$(docker inspect -f '{{range .Mounts}}{{println .Source}}{{end}}' "$c" 2>/dev/null)" || continue
|
|
402
|
+
if { [ -n "$base" ] && grep -q "^$base/" <<<"$src"; } || grep -q "^$tt/" <<<"$src"; then s="$s $c"; fi
|
|
403
|
+
done
|
|
404
|
+
if [ -n "$s" ]; then
|
|
405
|
+
echo "::warning::removing containers this job left running:$s"
|
|
406
|
+
docker rm -f $s
|
|
407
|
+
fi
|
|
390
408
|
|
|
391
409
|
# Report this pipeline's result to the aggregated product CI dashboard
|
|
392
410
|
# (id3as/ci-workflows). !cancelled() so a real pass/fail reports, but a
|
|
@@ -469,6 +487,24 @@ jobs:
|
|
|
469
487
|
product: __PRODUCT__
|
|
470
488
|
pipeline: integration
|
|
471
489
|
status: ${{ steps.meta.outputs.state }}
|
|
490
|
+
- name: Reap the containers this job launched
|
|
491
|
+
# Whatever the tests did. A teardown cut short by a test timeout leaves
|
|
492
|
+
# its instance running on the shared Docker daemon (see JOB_REAP_STEP in
|
|
493
|
+
# conventions/workflow-shell.ts); everything this job launched mounts its
|
|
494
|
+
# own test-temp, so that is what goes.
|
|
495
|
+
if: always()
|
|
496
|
+
run: |
|
|
497
|
+
base="${NORSK_CTL_TEST_TMP:-}"
|
|
498
|
+
tt="$GITHUB_WORKSPACE/test-temp"
|
|
499
|
+
s=""
|
|
500
|
+
for c in $(docker ps -aq); do
|
|
501
|
+
src="$(docker inspect -f '{{range .Mounts}}{{println .Source}}{{end}}' "$c" 2>/dev/null)" || continue
|
|
502
|
+
if { [ -n "$base" ] && grep -q "^$base/" <<<"$src"; } || grep -q "^$tt/" <<<"$src"; then s="$s $c"; fi
|
|
503
|
+
done
|
|
504
|
+
if [ -n "$s" ]; then
|
|
505
|
+
echo "::warning::removing containers this job left running:$s"
|
|
506
|
+
docker rm -f $s
|
|
507
|
+
fi
|
|
472
508
|
|
|
473
509
|
# Report the rc-candidate validation result to the norsk-ctl release gate. Only
|
|
474
510
|
# on a ctl-candidate dispatch; release-collect tallies these and promotes
|
|
@@ -319,3 +319,21 @@ jobs:
|
|
|
319
319
|
"https://api.github.com/repos/id3as/norsk-ctl/dispatches" -d "$body")"
|
|
320
320
|
[ "$code" = "204" ] || { echo "::error::docs-published dispatch to norsk-ctl failed (HTTP $code)"; exit 1; }
|
|
321
321
|
echo "dispatched product-docs-published -> id3as/norsk-ctl"
|
|
322
|
+
- name: Reap the containers this job launched
|
|
323
|
+
# Whatever the tests did. A teardown cut short by a test timeout leaves
|
|
324
|
+
# its instance running on the shared Docker daemon (see JOB_REAP_STEP in
|
|
325
|
+
# conventions/workflow-shell.ts); everything this job launched mounts its
|
|
326
|
+
# own test-temp, so that is what goes.
|
|
327
|
+
if: always()
|
|
328
|
+
run: |
|
|
329
|
+
base="${NORSK_CTL_TEST_TMP:-}"
|
|
330
|
+
tt="$GITHUB_WORKSPACE/test-temp"
|
|
331
|
+
s=""
|
|
332
|
+
for c in $(docker ps -aq); do
|
|
333
|
+
src="$(docker inspect -f '{{range .Mounts}}{{println .Source}}{{end}}' "$c" 2>/dev/null)" || continue
|
|
334
|
+
if { [ -n "$base" ] && grep -q "^$base/" <<<"$src"; } || grep -q "^$tt/" <<<"$src"; then s="$s $c"; fi
|
|
335
|
+
done
|
|
336
|
+
if [ -n "$s" ]; then
|
|
337
|
+
echo "::warning::removing containers this job left running:$s"
|
|
338
|
+
docker rm -f $s
|
|
339
|
+
fi
|
package/conventions/smoke.yml
CHANGED
|
@@ -326,6 +326,24 @@ jobs:
|
|
|
326
326
|
path: ${{ runner.temp }}/norsk-logs-${{ github.run_id }}-${{ github.run_attempt }}
|
|
327
327
|
retention-days: 7
|
|
328
328
|
if-no-files-found: ignore
|
|
329
|
+
- name: Reap the containers this job launched
|
|
330
|
+
# Whatever the tests did. A teardown cut short by a test timeout leaves
|
|
331
|
+
# its instance running on the shared Docker daemon (see JOB_REAP_STEP in
|
|
332
|
+
# conventions/workflow-shell.ts); everything this job launched mounts its
|
|
333
|
+
# own test-temp, so that is what goes.
|
|
334
|
+
if: always()
|
|
335
|
+
run: |
|
|
336
|
+
base="${NORSK_CTL_TEST_TMP:-}"
|
|
337
|
+
tt="$GITHUB_WORKSPACE/test-temp"
|
|
338
|
+
s=""
|
|
339
|
+
for c in $(docker ps -aq); do
|
|
340
|
+
src="$(docker inspect -f '{{range .Mounts}}{{println .Source}}{{end}}' "$c" 2>/dev/null)" || continue
|
|
341
|
+
if { [ -n "$base" ] && grep -q "^$base/" <<<"$src"; } || grep -q "^$tt/" <<<"$src"; then s="$s $c"; fi
|
|
342
|
+
done
|
|
343
|
+
if [ -n "$s" ]; then
|
|
344
|
+
echo "::warning::removing containers this job left running:$s"
|
|
345
|
+
docker rm -f $s
|
|
346
|
+
fi
|
|
329
347
|
|
|
330
348
|
# Report this pipeline's result to the aggregated product CI dashboard
|
|
331
349
|
# (id3as/ci-workflows). !cancelled() so a real pass/fail reports, but a
|
|
@@ -408,6 +426,24 @@ jobs:
|
|
|
408
426
|
product: __PRODUCT__
|
|
409
427
|
pipeline: smoke
|
|
410
428
|
status: ${{ steps.meta.outputs.state }}
|
|
429
|
+
- name: Reap the containers this job launched
|
|
430
|
+
# Whatever the tests did. A teardown cut short by a test timeout leaves
|
|
431
|
+
# its instance running on the shared Docker daemon (see JOB_REAP_STEP in
|
|
432
|
+
# conventions/workflow-shell.ts); everything this job launched mounts its
|
|
433
|
+
# own test-temp, so that is what goes.
|
|
434
|
+
if: always()
|
|
435
|
+
run: |
|
|
436
|
+
base="${NORSK_CTL_TEST_TMP:-}"
|
|
437
|
+
tt="$GITHUB_WORKSPACE/test-temp"
|
|
438
|
+
s=""
|
|
439
|
+
for c in $(docker ps -aq); do
|
|
440
|
+
src="$(docker inspect -f '{{range .Mounts}}{{println .Source}}{{end}}' "$c" 2>/dev/null)" || continue
|
|
441
|
+
if { [ -n "$base" ] && grep -q "^$base/" <<<"$src"; } || grep -q "^$tt/" <<<"$src"; then s="$s $c"; fi
|
|
442
|
+
done
|
|
443
|
+
if [ -n "$s" ]; then
|
|
444
|
+
echo "::warning::removing containers this job left running:$s"
|
|
445
|
+
docker rm -f $s
|
|
446
|
+
fi
|
|
411
447
|
|
|
412
448
|
# Report the rc-candidate validation result to the norsk-ctl release gate. Only
|
|
413
449
|
# on a ctl-candidate dispatch; release-collect tallies these and promotes
|
|
@@ -160,10 +160,13 @@ jobs:
|
|
|
160
160
|
{ echo "moved<<PINS"; printf '%s' "$moved"; echo "PINS"; } >> "$GITHUB_OUTPUT"
|
|
161
161
|
fi
|
|
162
162
|
|
|
163
|
-
- name:
|
|
164
|
-
id: gate
|
|
163
|
+
- name: Refresh the lockfiles against the new pins
|
|
165
164
|
if: steps.bump.outputs.changed == '1'
|
|
166
|
-
continue-on-error
|
|
165
|
+
# NOT continue-on-error, unlike the gate below. A refresh that dies
|
|
166
|
+
# part-way (the runner OOM-killed it on 2026-10-03) leaves bumped
|
|
167
|
+
# manifests beside the old lockfiles; carrying on then committed exactly
|
|
168
|
+
# that, and every frozen install on the PR failed. Fail here instead,
|
|
169
|
+
# before anything is committed.
|
|
167
170
|
run: |
|
|
168
171
|
nix develop .#build --command bash -c '
|
|
169
172
|
set -euo pipefail
|
|
@@ -198,6 +201,15 @@ jobs:
|
|
|
198
201
|
(cd "$(dirname "$lock")" && bun update "@norskvideo/$pkg") || true
|
|
199
202
|
done
|
|
200
203
|
done
|
|
204
|
+
'
|
|
205
|
+
|
|
206
|
+
- name: Run the product gate
|
|
207
|
+
id: gate
|
|
208
|
+
if: steps.bump.outputs.changed == '1'
|
|
209
|
+
continue-on-error: true
|
|
210
|
+
run: |
|
|
211
|
+
nix develop .#build --command bash -c '
|
|
212
|
+
set -euo pipefail
|
|
201
213
|
bun run check:drift
|
|
202
214
|
bun run lint
|
|
203
215
|
bun run typecheck
|
|
@@ -276,3 +288,21 @@ jobs:
|
|
|
276
288
|
curl "${curl_api[@]}" -X POST "$api" \
|
|
277
289
|
--data "$(jq -nc --arg t "$title" --arg b "$body" --arg h "$branch" --argjson d "$draft" '{title:$t, body:$b, head:$h, base:"main", draft:$d}')" >/dev/null
|
|
278
290
|
fi
|
|
291
|
+
- name: Reap the containers this job launched
|
|
292
|
+
# Whatever the tests did. A teardown cut short by a test timeout leaves
|
|
293
|
+
# its instance running on the shared Docker daemon (see JOB_REAP_STEP in
|
|
294
|
+
# conventions/workflow-shell.ts); everything this job launched mounts its
|
|
295
|
+
# own test-temp, so that is what goes.
|
|
296
|
+
if: always()
|
|
297
|
+
run: |
|
|
298
|
+
base="${NORSK_CTL_TEST_TMP:-}"
|
|
299
|
+
tt="$GITHUB_WORKSPACE/test-temp"
|
|
300
|
+
s=""
|
|
301
|
+
for c in $(docker ps -aq); do
|
|
302
|
+
src="$(docker inspect -f '{{range .Mounts}}{{println .Source}}{{end}}' "$c" 2>/dev/null)" || continue
|
|
303
|
+
if { [ -n "$base" ] && grep -q "^$base/" <<<"$src"; } || grep -q "^$tt/" <<<"$src"; then s="$s $c"; fi
|
|
304
|
+
done
|
|
305
|
+
if [ -n "$s" ]; then
|
|
306
|
+
echo "::warning::removing containers this job left running:$s"
|
|
307
|
+
docker rm -f $s
|
|
308
|
+
fi
|
|
@@ -139,14 +139,25 @@ jobs:
|
|
|
139
139
|
echo "old=$old_bare" >> "$GITHUB_OUTPUT"
|
|
140
140
|
echo "new=$new" >> "$GITHUB_OUTPUT"
|
|
141
141
|
|
|
142
|
-
- name: Reinstall
|
|
142
|
+
- name: Reinstall against the new pin
|
|
143
|
+
if: steps.sync.outputs.changed == '1'
|
|
144
|
+
# NOT continue-on-error, unlike the gate below. An install that dies
|
|
145
|
+
# part-way (the runner OOM-killed sync-ctl-packages' one on 2026-10-03)
|
|
146
|
+
# would otherwise read as a red gate and open a reconciliation PR around
|
|
147
|
+
# a stale lockfile. Fail here instead, before anything is committed.
|
|
148
|
+
run: |
|
|
149
|
+
nix develop .#build --command bash -c '
|
|
150
|
+
set -euo pipefail
|
|
151
|
+
bun install
|
|
152
|
+
'
|
|
153
|
+
|
|
154
|
+
- name: Re-sync the copies from canonical and run the product gate
|
|
143
155
|
id: gate
|
|
144
156
|
if: steps.sync.outputs.changed == '1'
|
|
145
157
|
continue-on-error: true
|
|
146
158
|
run: |
|
|
147
159
|
nix develop .#build --command bash -c '
|
|
148
160
|
set -euo pipefail
|
|
149
|
-
bun install
|
|
150
161
|
bun run node_modules/@norskvideo/ctl-dev-kit/conventions/sync-drift.ts
|
|
151
162
|
bun run check:drift
|
|
152
163
|
bun run lint
|
|
@@ -228,6 +239,24 @@ jobs:
|
|
|
228
239
|
- name: Report "already fresh"
|
|
229
240
|
if: steps.sync.outputs.changed != '1'
|
|
230
241
|
run: echo "dev-kit pin already current -- nothing to sync."
|
|
242
|
+
- name: Reap the containers this job launched
|
|
243
|
+
# Whatever the tests did. A teardown cut short by a test timeout leaves
|
|
244
|
+
# its instance running on the shared Docker daemon (see JOB_REAP_STEP in
|
|
245
|
+
# conventions/workflow-shell.ts); everything this job launched mounts its
|
|
246
|
+
# own test-temp, so that is what goes.
|
|
247
|
+
if: always()
|
|
248
|
+
run: |
|
|
249
|
+
base="${NORSK_CTL_TEST_TMP:-}"
|
|
250
|
+
tt="$GITHUB_WORKSPACE/test-temp"
|
|
251
|
+
s=""
|
|
252
|
+
for c in $(docker ps -aq); do
|
|
253
|
+
src="$(docker inspect -f '{{range .Mounts}}{{println .Source}}{{end}}' "$c" 2>/dev/null)" || continue
|
|
254
|
+
if { [ -n "$base" ] && grep -q "^$base/" <<<"$src"; } || grep -q "^$tt/" <<<"$src"; then s="$s $c"; fi
|
|
255
|
+
done
|
|
256
|
+
if [ -n "$s" ]; then
|
|
257
|
+
echo "::warning::removing containers this job left running:$s"
|
|
258
|
+
docker rm -f $s
|
|
259
|
+
fi
|
|
231
260
|
|
|
232
261
|
# Report the sync result to the aggregated product CI dashboard
|
|
233
262
|
# (id3as/ci-workflows). !cancelled() so a failed sync shows a sad pony but a
|
|
@@ -311,3 +340,21 @@ jobs:
|
|
|
311
340
|
product: __PRODUCT__
|
|
312
341
|
pipeline: dev-kit
|
|
313
342
|
status: ${{ steps.meta.outputs.state }}
|
|
343
|
+
- name: Reap the containers this job launched
|
|
344
|
+
# Whatever the tests did. A teardown cut short by a test timeout leaves
|
|
345
|
+
# its instance running on the shared Docker daemon (see JOB_REAP_STEP in
|
|
346
|
+
# conventions/workflow-shell.ts); everything this job launched mounts its
|
|
347
|
+
# own test-temp, so that is what goes.
|
|
348
|
+
if: always()
|
|
349
|
+
run: |
|
|
350
|
+
base="${NORSK_CTL_TEST_TMP:-}"
|
|
351
|
+
tt="$GITHUB_WORKSPACE/test-temp"
|
|
352
|
+
s=""
|
|
353
|
+
for c in $(docker ps -aq); do
|
|
354
|
+
src="$(docker inspect -f '{{range .Mounts}}{{println .Source}}{{end}}' "$c" 2>/dev/null)" || continue
|
|
355
|
+
if { [ -n "$base" ] && grep -q "^$base/" <<<"$src"; } || grep -q "^$tt/" <<<"$src"; then s="$s $c"; fi
|
|
356
|
+
done
|
|
357
|
+
if [ -n "$s" ]; then
|
|
358
|
+
echo "::warning::removing containers this job left running:$s"
|
|
359
|
+
docker rm -f $s
|
|
360
|
+
fi
|
|
@@ -375,6 +375,24 @@ jobs:
|
|
|
375
375
|
- name: Report "already fresh"
|
|
376
376
|
if: steps.bump.outputs.changed != '1'
|
|
377
377
|
run: echo "Nightly pins already current — nothing to bump."
|
|
378
|
+
- name: Reap the containers this job launched
|
|
379
|
+
# Whatever the tests did. A teardown cut short by a test timeout leaves
|
|
380
|
+
# its instance running on the shared Docker daemon (see JOB_REAP_STEP in
|
|
381
|
+
# conventions/workflow-shell.ts); everything this job launched mounts its
|
|
382
|
+
# own test-temp, so that is what goes.
|
|
383
|
+
if: always()
|
|
384
|
+
run: |
|
|
385
|
+
base="${NORSK_CTL_TEST_TMP:-}"
|
|
386
|
+
tt="$GITHUB_WORKSPACE/test-temp"
|
|
387
|
+
s=""
|
|
388
|
+
for c in $(docker ps -aq); do
|
|
389
|
+
src="$(docker inspect -f '{{range .Mounts}}{{println .Source}}{{end}}' "$c" 2>/dev/null)" || continue
|
|
390
|
+
if { [ -n "$base" ] && grep -q "^$base/" <<<"$src"; } || grep -q "^$tt/" <<<"$src"; then s="$s $c"; fi
|
|
391
|
+
done
|
|
392
|
+
if [ -n "$s" ]; then
|
|
393
|
+
echo "::warning::removing containers this job left running:$s"
|
|
394
|
+
docker rm -f $s
|
|
395
|
+
fi
|
|
378
396
|
|
|
379
397
|
# Report the nightly bump result to the aggregated product CI dashboard
|
|
380
398
|
# (id3as/ci-workflows). !cancelled() so a failed bump shows a sad pony (a
|
|
@@ -458,3 +476,21 @@ jobs:
|
|
|
458
476
|
pipeline: nightly
|
|
459
477
|
status: ${{ steps.meta.outputs.state }}
|
|
460
478
|
detail: ${{ needs.bump.outputs.summary }}
|
|
479
|
+
- name: Reap the containers this job launched
|
|
480
|
+
# Whatever the tests did. A teardown cut short by a test timeout leaves
|
|
481
|
+
# its instance running on the shared Docker daemon (see JOB_REAP_STEP in
|
|
482
|
+
# conventions/workflow-shell.ts); everything this job launched mounts its
|
|
483
|
+
# own test-temp, so that is what goes.
|
|
484
|
+
if: always()
|
|
485
|
+
run: |
|
|
486
|
+
base="${NORSK_CTL_TEST_TMP:-}"
|
|
487
|
+
tt="$GITHUB_WORKSPACE/test-temp"
|
|
488
|
+
s=""
|
|
489
|
+
for c in $(docker ps -aq); do
|
|
490
|
+
src="$(docker inspect -f '{{range .Mounts}}{{println .Source}}{{end}}' "$c" 2>/dev/null)" || continue
|
|
491
|
+
if { [ -n "$base" ] && grep -q "^$base/" <<<"$src"; } || grep -q "^$tt/" <<<"$src"; then s="$s $c"; fi
|
|
492
|
+
done
|
|
493
|
+
if [ -n "$s" ]; then
|
|
494
|
+
echo "::warning::removing containers this job left running:$s"
|
|
495
|
+
docker rm -f $s
|
|
496
|
+
fi
|
|
@@ -171,6 +171,100 @@ export function jobsMissingTestTempStep(yaml: string): string[] {
|
|
|
171
171
|
return bad;
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
+
/** The step every job that routes test-temp ends with, named so the guards
|
|
175
|
+
* below can find it. */
|
|
176
|
+
export const JOB_REAP_STEP = "Reap the containers this job launched";
|
|
177
|
+
|
|
178
|
+
// Why a job needs a reap of its own at the END, when the pre-checkout step
|
|
179
|
+
// already reaps. The pre-checkout reap only protects the NEXT job's checkout,
|
|
180
|
+
// and only from containers mounting the legacy in-workspace test-temp. A
|
|
181
|
+
// harness teardown that never finishes leaves the instance running on the
|
|
182
|
+
// shared Docker daemon until something else removes it: bun times a test out,
|
|
183
|
+
// kills the daemon with the test's other dangling processes, and the afterAll
|
|
184
|
+
// that should delete the instance has nothing left to ask, then times out
|
|
185
|
+
// itself. commentary's publish-docs left a composition guide's engine running
|
|
186
|
+
// that way for 2.6 hours on 2026-10-03, where it grew to 28 GB and the host
|
|
187
|
+
// OOM-killed the next morning's product CI. The job knows exactly what it
|
|
188
|
+
// launched -- everything mounting its own $NORSK_CTL_TEST_TMP -- so the job
|
|
189
|
+
// removes it, whatever happened before.
|
|
190
|
+
const JOB_REAP_REQUIREMENTS: { name: string; ok: (body: string) => boolean }[] = [
|
|
191
|
+
{ name: "scope", ok: (b) => /\$\{?NORSK_CTL_TEST_TMP/.test(b) && /\.Mounts\b/.test(b) },
|
|
192
|
+
{ name: "remove", ok: (b) => /docker rm -f/.test(b) },
|
|
193
|
+
];
|
|
194
|
+
|
|
195
|
+
/** {@link JOB_REAP_STEP}s that would not reap, each reported at its `- name:`
|
|
196
|
+
* line with what it is missing: the `always()` guard (without it a failed test
|
|
197
|
+
* skips the step, which is exactly when it is needed) or a requirement above. */
|
|
198
|
+
export function jobReapStepProblems(yaml: string): { line: number; missing: string[] }[] {
|
|
199
|
+
const lines = yaml.split("\n");
|
|
200
|
+
const out: { line: number; missing: string[] }[] = [];
|
|
201
|
+
lines.forEach((text, i) => {
|
|
202
|
+
if (!text.includes(`- name: ${JOB_REAP_STEP}`)) return;
|
|
203
|
+
const missing: string[] = [];
|
|
204
|
+
const runAt = lines.findIndex((l, j) => j > i && /^\s*run: \|\s*$/.test(l));
|
|
205
|
+
const header = lines.slice(i + 1, runAt < 0 ? i + 4 : runAt);
|
|
206
|
+
if (!header.some((l) => /^\s*if: always\(\)\s*$/.test(l))) missing.push("always");
|
|
207
|
+
const body = stepBody(lines, i);
|
|
208
|
+
missing.push(...JOB_REAP_REQUIREMENTS.filter((r) => !r.ok(body)).map((r) => r.name));
|
|
209
|
+
if (missing.length > 0) out.push({ line: i + 1, missing });
|
|
210
|
+
});
|
|
211
|
+
return out;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** Jobs that route test-temp (so may launch instances) but have no
|
|
215
|
+
* {@link JOB_REAP_STEP}. The presence half, per job for the same reason as
|
|
216
|
+
* {@link jobsMissingTestTempStep}. */
|
|
217
|
+
export function jobsMissingReapStep(yaml: string): string[] {
|
|
218
|
+
const lines = yaml.split("\n");
|
|
219
|
+
const bad: string[] = [];
|
|
220
|
+
let job: string | undefined;
|
|
221
|
+
let routes = 0;
|
|
222
|
+
let reaps = 0;
|
|
223
|
+
const settle = () => {
|
|
224
|
+
if (job !== undefined && routes > 0 && reaps === 0) bad.push(job);
|
|
225
|
+
};
|
|
226
|
+
for (const line of lines) {
|
|
227
|
+
const head = line.match(/^ {2}([A-Za-z_][\w-]*):\s*$/);
|
|
228
|
+
if (head) {
|
|
229
|
+
settle();
|
|
230
|
+
job = head[1];
|
|
231
|
+
routes = 0;
|
|
232
|
+
reaps = 0;
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
if (line.includes(`- name: ${TEST_TEMP_STEP}`)) routes++;
|
|
236
|
+
if (line.includes(`- name: ${JOB_REAP_STEP}`)) reaps++;
|
|
237
|
+
}
|
|
238
|
+
settle();
|
|
239
|
+
return bad;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/** Steps that rewrite a lockfile -- an unfrozen `bun install`, or a
|
|
243
|
+
* `bun update` -- yet are `continue-on-error`, by their `- name:` line.
|
|
244
|
+
*
|
|
245
|
+
* A gate may be allowed to fail: a red gate is information, and the sync
|
|
246
|
+
* workflows turn it into a draft or reconciliation PR. A lockfile refresh may
|
|
247
|
+
* not. One that dies part-way leaves bumped manifests beside the old
|
|
248
|
+
* lockfiles, and a later step commits that pair: on 2026-10-03 the runner
|
|
249
|
+
* OOM-killed sync-ctl-packages mid-install and its PR then failed every
|
|
250
|
+
* frozen install. Keep the refresh in a step of its own that must succeed. */
|
|
251
|
+
export function lockfileRefreshCanFail(yaml: string): number[] {
|
|
252
|
+
const lines = yaml.split("\n");
|
|
253
|
+
const starts = lines.flatMap((l, i) => (/^\s{6}- (name|uses|run)\b/.test(l) ? [i] : []));
|
|
254
|
+
const out: number[] = [];
|
|
255
|
+
starts.forEach((a, k) => {
|
|
256
|
+
const block = lines.slice(a, starts[k + 1] ?? lines.length);
|
|
257
|
+
const tolerated = block.some((l) => /^\s+continue-on-error:\s*true\b/.test(l));
|
|
258
|
+
const rewrites = block.some((l) => {
|
|
259
|
+
const t = l.trim();
|
|
260
|
+
if (t.startsWith("#") || t.startsWith('"')) return false;
|
|
261
|
+
return /\bbun (install|update)\b/.test(t) && !/--frozen-lockfile/.test(t);
|
|
262
|
+
});
|
|
263
|
+
if (tolerated && rewrites) out.push(a + 1);
|
|
264
|
+
});
|
|
265
|
+
return out;
|
|
266
|
+
}
|
|
267
|
+
|
|
174
268
|
/** A `ctl-candidate` validation that another candidate, or its own retry, can
|
|
175
269
|
* cancel.
|
|
176
270
|
*
|