@norskvideo/ctl-dev-kit 0.2.63 → 0.2.65
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/build/flake.nix +1 -1
- package/build/flake.sdk-app.nix +2 -2
- package/build/refresh-ctl-pin.sh +1 -1
- package/conventions/CLAUDE.core.md +16 -7
- package/conventions/bot-prs.yml +91 -61
- package/conventions/build-docs.yml +27 -2
- package/conventions/build-image.yml +290 -22
- package/conventions/bump.sdk-app.yml +808 -0
- package/conventions/bump.yml +916 -0
- package/conventions/check-drift.ts +52 -60
- package/conventions/checks.sdk-app.yml +20 -1
- package/conventions/checks.yml +20 -1
- package/conventions/ctl-pin-agreement.ts +3 -4
- package/conventions/docs-site.md +3 -3
- package/conventions/integration-testing.md +1 -1
- package/conventions/integration.yml +31 -5
- package/conventions/publish-docs.yml +107 -12
- package/conventions/quarantine.ts +209 -0
- package/conventions/smoke.yml +27 -2
- package/conventions/sync-drift.ts +61 -24
- package/create-product/canon.ts +2 -6
- package/create-product/create-product.ts +1 -3
- package/create-product/turnkey.ts +2 -2
- package/doc-guide/engine-verdict.js +3 -3
- package/docs-theme/README.md +1 -1
- package/package.json +1 -1
- package/conventions/sync-ctl-packages.yml +0 -309
- package/conventions/sync-dev-kit.yml +0 -404
- package/conventions/upgrade-latest.sdk-app.yml +0 -456
- package/conventions/upgrade-latest.yml +0 -567
|
@@ -21,9 +21,11 @@
|
|
|
21
21
|
# (rc -> latest promotion) lands, flip the push default to `nightly` and let the
|
|
22
22
|
# promotion gate move `latest`.
|
|
23
23
|
#
|
|
24
|
-
# BOT PRs: on a pull request from a bot branch (
|
|
25
|
-
# sync-dev-kit
|
|
26
|
-
#
|
|
24
|
+
# BOT PRs: on a pull request from a bot branch (bump/nightly, and the retired
|
|
25
|
+
# bots' upgrade-latest/*, sync-dev-kit/* and chore/sync-ctl-packages, which the
|
|
26
|
+
# PR that brings bump.yml rides on) the workflow builds both arches and
|
|
27
|
+
# runs the guides gate exactly as a push would (the deterministic tiers gate;
|
|
28
|
+
# the engine tier reports beside it), and publishes, records and
|
|
27
29
|
# reports nothing. A bump reaches main only once that has passed on it, and
|
|
28
30
|
# bot-prs merges on it (ci-workflows docs/dashboard/ci-fragility.md, proposal
|
|
29
31
|
# B): a dev-kit sync that brought a newer copy of THIS file broke
|
|
@@ -68,7 +70,7 @@ jobs:
|
|
|
68
70
|
# Compute the immutable label ONCE, so both arch builds and the manifest share
|
|
69
71
|
# it (a per-job `date` could straddle midnight and disagree).
|
|
70
72
|
label:
|
|
71
|
-
if: ${{ github.event_name != 'pull_request' || (github.event.pull_request.head.repo.full_name == github.repository && (startsWith(github.head_ref, 'upgrade-latest/') || startsWith(github.head_ref, 'sync-dev-kit/') || github.head_ref == 'chore/sync-ctl-packages')) }}
|
|
73
|
+
if: ${{ github.event_name != 'pull_request' || (github.event.pull_request.head.repo.full_name == github.repository && (github.head_ref == 'bump/nightly' || startsWith(github.head_ref, 'upgrade-latest/') || startsWith(github.head_ref, 'sync-dev-kit/') || github.head_ref == 'chore/sync-ctl-packages')) }}
|
|
72
74
|
runs-on: x64
|
|
73
75
|
outputs:
|
|
74
76
|
label: ${{ steps.gen.outputs.label }}
|
|
@@ -302,15 +304,13 @@ jobs:
|
|
|
302
304
|
gate-guides:
|
|
303
305
|
needs: label
|
|
304
306
|
runs-on: x64
|
|
305
|
-
#
|
|
306
|
-
#
|
|
307
|
-
#
|
|
308
|
-
concurrency:
|
|
309
|
-
group: doc-guides-${{ github.repository }}
|
|
310
|
-
cancel-in-progress: false
|
|
307
|
+
# No doc-guides concurrency group: the deterministic tiers launch no
|
|
308
|
+
# instances, so there is nothing to serialise, and a pending job in a group
|
|
309
|
+
# is cancelled by the next one queued. guides-engine takes the group.
|
|
311
310
|
outputs:
|
|
312
311
|
guides: ${{ steps.plan.outputs.guides }}
|
|
313
312
|
reason: ${{ steps.plan.outputs.reason }}
|
|
313
|
+
engine: ${{ steps.plan.outputs.engine }}
|
|
314
314
|
steps:
|
|
315
315
|
# Runs BEFORE checkout, and does two things: points the harness's temp base
|
|
316
316
|
# out of the workspace for every later step in this job, and clears whatever
|
|
@@ -390,6 +390,10 @@ jobs:
|
|
|
390
390
|
')"
|
|
391
391
|
if [ "$decision" = "run" ]; then
|
|
392
392
|
echo "guides=run" >> "$GITHUB_OUTPUT"
|
|
393
|
+
# The engine tier runs in guides-engine, beside this gate.
|
|
394
|
+
if jq -e '.scripts["docs:guides:engine"]' package.json >/dev/null; then
|
|
395
|
+
echo "engine=run" >> "$GITHUB_OUTPUT"
|
|
396
|
+
fi
|
|
393
397
|
else
|
|
394
398
|
skip "${decision#skip$'\t'}"
|
|
395
399
|
fi
|
|
@@ -401,8 +405,33 @@ jobs:
|
|
|
401
405
|
NORSK_LICENSE_V2: ${{ secrets.NORSK_LICENSE_V2 }}
|
|
402
406
|
run: printf '%s' "$NORSK_LICENSE_V2" > "$RUNNER_TEMP/norsk-license.json"
|
|
403
407
|
|
|
408
|
+
# Every norsk-ctl daemon starts the node MoQ relay (norsk-moq-relay) unless
|
|
409
|
+
# told not to, and nothing removes it: it is a node singleton, and the reap
|
|
410
|
+
# step only takes containers that mount this job's test-temp. So the first
|
|
411
|
+
# test daemon on a shared runner left one, and every later one adopted it
|
|
412
|
+
# (probe's smoke ran in relay mode on 2026-10-10; relay's smoke lost 4443
|
|
413
|
+
# to it). This job's daemons run without the node relay and discovery, so
|
|
414
|
+
# they neither start nor adopt one, and whatever is already on the host is
|
|
415
|
+
# named here. It reports and never fails: the leftover may be someone's.
|
|
416
|
+
- name: Isolate this job from node singletons other jobs left on this host
|
|
417
|
+
run: |
|
|
418
|
+
set -uo pipefail
|
|
419
|
+
echo "NORSK_CTL_MOQ_RELAY_ENABLE=0" >> "$GITHUB_ENV"
|
|
420
|
+
echo "NORSK_CTL_DISCOVERY_ENABLE=0" >> "$GITHUB_ENV"
|
|
421
|
+
left="$(docker ps -a --filter label=norsk-ctl.role --format '{{.Names}} {{.Label "norsk-ctl.role"}} {{.Status}} (created {{.CreatedAt}})' 2>/dev/null || true)"
|
|
422
|
+
if [ -z "$left" ]; then echo "no norsk-ctl containers on this host"; exit 0; fi
|
|
423
|
+
echo "norsk-ctl containers already on this host (other jobs' running instances are expected):"
|
|
424
|
+
echo "$left"
|
|
425
|
+
while IFS= read -r line; do
|
|
426
|
+
role="$(awk '{print $2}' <<<"$line")"
|
|
427
|
+
case "$role" in
|
|
428
|
+
moq-relay|discovery)
|
|
429
|
+
echo "::warning title=node singleton left on this host::$line -- left by an earlier job (a daemon's auto-start, or a run killed before its reap). This job's daemons run without it and will not adopt it; remove it with docker rm -f if nothing on this host needs it." ;;
|
|
430
|
+
esac
|
|
431
|
+
done <<<"$left"
|
|
432
|
+
|
|
404
433
|
# The norsk-ctl this commit pins (flake.nix ctlVersion, moved by
|
|
405
|
-
#
|
|
434
|
+
# bump.yml's nightly PR), never S3's moving `latest`: a guide run is then
|
|
406
435
|
# a real repeat, and a red belongs to a commit (ci-fragility proposal A).
|
|
407
436
|
- name: Download the pinned norsk-ctl binary (flake.nix ctlVersion)
|
|
408
437
|
if: ${{ steps.plan.outputs.guides == 'run' }}
|
|
@@ -410,7 +439,7 @@ jobs:
|
|
|
410
439
|
set -euo pipefail
|
|
411
440
|
S3="https://s3.eu-west-1.amazonaws.com/norsk.video/norsk-ctl"
|
|
412
441
|
ver="$( { sed -nE 's/^[[:space:]]*ctlVersion = "([^"]+)";.*/\1/p' flake.nix 2>/dev/null || true; } | head -1)"
|
|
413
|
-
[ -n "$ver" ] || { echo "::error::flake.nix pins no ctlVersion. CI runs the norsk-ctl this commit pins (
|
|
442
|
+
[ -n "$ver" ] || { echo "::error::flake.nix pins no ctlVersion. CI runs the norsk-ctl this commit pins (the nightly bump.yml moves it); add ctlVersion = \"<version>\"; to flake.nix."; exit 1; }
|
|
414
443
|
echo "norsk-ctl pinned by flake.nix ctlVersion -> $ver"
|
|
415
444
|
curl -fsSL --retry 3 --retry-all-errors --retry-delay 2 "$S3/$ver/norsk-ctl-$ver-linux-x64" -o "$RUNNER_TEMP/norsk-ctl"
|
|
416
445
|
chmod +x "$RUNNER_TEMP/norsk-ctl"
|
|
@@ -433,13 +462,15 @@ jobs:
|
|
|
433
462
|
./scripts/doc-guide/prepull-extra.sh
|
|
434
463
|
fi
|
|
435
464
|
|
|
436
|
-
#
|
|
437
|
-
# the
|
|
438
|
-
#
|
|
439
|
-
#
|
|
440
|
-
#
|
|
441
|
-
#
|
|
442
|
-
|
|
465
|
+
# The deterministic tiers (fixture and frontend: no daemon, no instances),
|
|
466
|
+
# then the manual assembled from what they captured. These gate. The engine
|
|
467
|
+
# tier runs on its own below: it stands up real instances, is known-flaky
|
|
468
|
+
# on container DNS (norsk-ctl task 57), and reports without gating until
|
|
469
|
+
# that is fixed (ci-workflows docs/dashboard/ci-fragility.md, proposal E).
|
|
470
|
+
# regen-manual.sh owns the slug list and honours SKIP_ENGINE; it rebuilds
|
|
471
|
+
# every workspace the manual depends on first, and with-display.sh gives
|
|
472
|
+
# headless chromium an X display.
|
|
473
|
+
- name: Regenerate the illustrated manual (deterministic tiers)
|
|
443
474
|
if: ${{ steps.plan.outputs.guides == 'run' }}
|
|
444
475
|
env:
|
|
445
476
|
NORSK_CTL_BINARY: ${{ runner.temp }}/norsk-ctl
|
|
@@ -453,6 +484,7 @@ jobs:
|
|
|
453
484
|
# them binding no host ports — so concurrent doc-guide instances can't
|
|
454
485
|
# collide on the product's deterministic host-port bands.
|
|
455
486
|
NORSK_TEST_NET: direct
|
|
487
|
+
SKIP_ENGINE: "1"
|
|
456
488
|
run: |
|
|
457
489
|
nix develop .#build --command bash -c '
|
|
458
490
|
set -euo pipefail
|
|
@@ -486,6 +518,228 @@ jobs:
|
|
|
486
518
|
docker rm -f $s
|
|
487
519
|
fi
|
|
488
520
|
|
|
521
|
+
# The engine tier of the guides, beside the gate rather than in it (see the
|
|
522
|
+
# deterministic step above): it runs whenever the gate runs the guides and the
|
|
523
|
+
# product has an engine tier, and reports -- a warning, the run summary and
|
|
524
|
+
# the build-image cell's detail -- but publish does not wait on it, and a
|
|
525
|
+
# failure leaves the job green so bot-prs can still merge. To make it gate
|
|
526
|
+
# again once norsk-ctl task 57 is fixed, add it to publish's needs and drop
|
|
527
|
+
# continue-on-error from the engine step.
|
|
528
|
+
guides-engine:
|
|
529
|
+
needs: [label, gate-guides]
|
|
530
|
+
# !cancelled(): a red deterministic gate must not hide the engine tier's
|
|
531
|
+
# result. The plan's output is set before any guide runs.
|
|
532
|
+
if: ${{ !cancelled() && needs.gate-guides.outputs.engine == 'run' }}
|
|
533
|
+
runs-on: x64
|
|
534
|
+
# One engine-tier run per repo at a time, nightly (publish-docs.yml) and
|
|
535
|
+
# this alike: both launch the same instance ids on the x64 pool's shared
|
|
536
|
+
# docker daemon, and ctl names each compose project after its instance id.
|
|
537
|
+
# Queue, never cancel.
|
|
538
|
+
concurrency:
|
|
539
|
+
group: doc-guides-${{ github.repository }}
|
|
540
|
+
cancel-in-progress: false
|
|
541
|
+
outputs:
|
|
542
|
+
result: ${{ steps.engine.outcome }}
|
|
543
|
+
steps:
|
|
544
|
+
# Runs BEFORE checkout, and does two things: points the harness's temp base
|
|
545
|
+
# out of the workspace for every later step in this job, and clears whatever
|
|
546
|
+
# the old repo-local default left behind on this runner. The body carries the
|
|
547
|
+
# why. It fails the job only when legacy litter survives -- which would take
|
|
548
|
+
# the checkout down a step later anyway, but report nothing useful.
|
|
549
|
+
- name: Route test-temp out of the workspace (pre-checkout)
|
|
550
|
+
run: |
|
|
551
|
+
set -uo pipefail
|
|
552
|
+
# These runners are docker-OUTSIDE-of-docker, so a bind-mount source the
|
|
553
|
+
# HOST daemon has not seen before is created by dockerd, as ROOT, on the
|
|
554
|
+
# host -- even when the launch passes --container-user (reproduced
|
|
555
|
+
# 2026-09-04). The harness's default base is repo-local for an OrbStack
|
|
556
|
+
# inotify constraint that binds macOS dev and nothing here, so every run
|
|
557
|
+
# seeded the CHECKOUT with root-owned dirs a non-root `git clean -ffdx`
|
|
558
|
+
# cannot remove. That took commentary's checks AND integration red on
|
|
559
|
+
# 2026-09-03. Use the sanctioned override, per-run and outside the
|
|
560
|
+
# checkout -- as 8ff17a8d did in June for one debug workflow, and stopped.
|
|
561
|
+
base="$RUNNER_TEMP/nctt-$GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT"
|
|
562
|
+
mkdir -p "$base"
|
|
563
|
+
echo "NORSK_CTL_TEST_TMP=$base" >> "$GITHUB_ENV"
|
|
564
|
+
# Yesterday's bases hold dockerd-created root dirs, so a non-root rm
|
|
565
|
+
# EACCESes. They cannot wedge a checkout, but they do fill the disk.
|
|
566
|
+
for old in $(find "$RUNNER_TEMP" -maxdepth 1 -name 'nctt-*' -mtime +0 -printf '%f\n' 2>/dev/null || true); do
|
|
567
|
+
docker run --rm --user 0:0 -v "$RUNNER_TEMP":/t alpine sh -c 'rm -rf -- "/t/$1"' sh "$old" >/dev/null 2>&1 || true
|
|
568
|
+
done
|
|
569
|
+
# LEGACY: a runner that ran the old default still carries a workspace
|
|
570
|
+
# test-temp, and a checkout with clean:true dies on it. Reap the holders
|
|
571
|
+
# first -- rm cannot unlink a live mountpoint even as root (EBUSY), and a
|
|
572
|
+
# RESTARTING leaked container re-creates the path as root seconds after
|
|
573
|
+
# any clean. Match on mount SOURCE, not a label: the holders are untracked
|
|
574
|
+
# by construction, because a killed run labels nothing.
|
|
575
|
+
tt="$GITHUB_WORKSPACE/test-temp"
|
|
576
|
+
[ -d "$tt" ] || exit 0
|
|
577
|
+
stuck=""
|
|
578
|
+
for c in $(docker ps -aq 2>/dev/null || true); do
|
|
579
|
+
if docker inspect -f '{{range .Mounts}}{{println .Source}}{{end}}' "$c" 2>/dev/null | grep -q "^$tt/"; then
|
|
580
|
+
stuck="$stuck $c"
|
|
581
|
+
fi
|
|
582
|
+
done
|
|
583
|
+
if [ -n "$stuck" ]; then
|
|
584
|
+
echo "reaping containers holding mounts under $tt:$stuck"
|
|
585
|
+
docker rm -f $stuck || true
|
|
586
|
+
fi
|
|
587
|
+
# The DIRECTORY, not just its contents: an empty but root-owned base
|
|
588
|
+
# still EACCESes a later mkdtemp. Mounting the PARENT is what lets a root
|
|
589
|
+
# container unlink the leaf.
|
|
590
|
+
docker run --rm --user 0:0 -v "$GITHUB_WORKSPACE":/w alpine \
|
|
591
|
+
sh -c 'rm -rf /w/test-temp' || rm -rf "$tt" || true
|
|
592
|
+
# Fail HERE if it survived. The checkout fails on it either way, but
|
|
593
|
+
# reports only an EACCES rmdir with no clue what was holding the path.
|
|
594
|
+
if [ -e "$tt" ]; then
|
|
595
|
+
echo "::error::$tt survived the pre-checkout clean"
|
|
596
|
+
ls -lan "$tt" || true
|
|
597
|
+
exit 1
|
|
598
|
+
fi
|
|
599
|
+
|
|
600
|
+
- uses: actions/checkout@v5
|
|
601
|
+
with:
|
|
602
|
+
clean: true
|
|
603
|
+
|
|
604
|
+
- name: Write the Norsk license (from the org secret)
|
|
605
|
+
env:
|
|
606
|
+
NORSK_LICENSE_V2: ${{ secrets.NORSK_LICENSE_V2 }}
|
|
607
|
+
run: printf '%s' "$NORSK_LICENSE_V2" > "$RUNNER_TEMP/norsk-license.json"
|
|
608
|
+
# Every norsk-ctl daemon starts the node MoQ relay (norsk-moq-relay) unless
|
|
609
|
+
# told not to, and nothing removes it: it is a node singleton, and the reap
|
|
610
|
+
# step only takes containers that mount this job's test-temp. So the first
|
|
611
|
+
# test daemon on a shared runner left one, and every later one adopted it
|
|
612
|
+
# (probe's smoke ran in relay mode on 2026-10-10; relay's smoke lost 4443
|
|
613
|
+
# to it). This job's daemons run without the node relay and discovery, so
|
|
614
|
+
# they neither start nor adopt one, and whatever is already on the host is
|
|
615
|
+
# named here. It reports and never fails: the leftover may be someone's.
|
|
616
|
+
- name: Isolate this job from node singletons other jobs left on this host
|
|
617
|
+
run: |
|
|
618
|
+
set -uo pipefail
|
|
619
|
+
echo "NORSK_CTL_MOQ_RELAY_ENABLE=0" >> "$GITHUB_ENV"
|
|
620
|
+
echo "NORSK_CTL_DISCOVERY_ENABLE=0" >> "$GITHUB_ENV"
|
|
621
|
+
left="$(docker ps -a --filter label=norsk-ctl.role --format '{{.Names}} {{.Label "norsk-ctl.role"}} {{.Status}} (created {{.CreatedAt}})' 2>/dev/null || true)"
|
|
622
|
+
if [ -z "$left" ]; then echo "no norsk-ctl containers on this host"; exit 0; fi
|
|
623
|
+
echo "norsk-ctl containers already on this host (other jobs' running instances are expected):"
|
|
624
|
+
echo "$left"
|
|
625
|
+
while IFS= read -r line; do
|
|
626
|
+
role="$(awk '{print $2}' <<<"$line")"
|
|
627
|
+
case "$role" in
|
|
628
|
+
moq-relay|discovery)
|
|
629
|
+
echo "::warning title=node singleton left on this host::$line -- left by an earlier job (a daemon's auto-start, or a run killed before its reap). This job's daemons run without it and will not adopt it; remove it with docker rm -f if nothing on this host needs it." ;;
|
|
630
|
+
esac
|
|
631
|
+
done <<<"$left"
|
|
632
|
+
|
|
633
|
+
# The norsk-ctl this commit pins (flake.nix ctlVersion, moved by
|
|
634
|
+
# bump.yml's nightly PR), never S3's moving `latest`: a guide run is then
|
|
635
|
+
# a real repeat, and a red belongs to a commit (ci-fragility proposal A).
|
|
636
|
+
- name: Download the pinned norsk-ctl binary (flake.nix ctlVersion)
|
|
637
|
+
run: |
|
|
638
|
+
set -euo pipefail
|
|
639
|
+
S3="https://s3.eu-west-1.amazonaws.com/norsk.video/norsk-ctl"
|
|
640
|
+
ver="$( { sed -nE 's/^[[:space:]]*ctlVersion = "([^"]+)";.*/\1/p' flake.nix 2>/dev/null || true; } | head -1)"
|
|
641
|
+
[ -n "$ver" ] || { echo "::error::flake.nix pins no ctlVersion. CI runs the norsk-ctl this commit pins (the nightly bump.yml moves it); add ctlVersion = \"<version>\"; to flake.nix."; exit 1; }
|
|
642
|
+
echo "norsk-ctl pinned by flake.nix ctlVersion -> $ver"
|
|
643
|
+
curl -fsSL --retry 3 --retry-all-errors --retry-delay 2 "$S3/$ver/norsk-ctl-$ver-linux-x64" -o "$RUNNER_TEMP/norsk-ctl"
|
|
644
|
+
chmod +x "$RUNNER_TEMP/norsk-ctl"
|
|
645
|
+
"$RUNNER_TEMP/norsk-ctl" --version || true
|
|
646
|
+
# Cold-runner first pulls overrun the harness's per-test launch timeouts;
|
|
647
|
+
# pre-pull the universal media + studio images (every product pins them in
|
|
648
|
+
# manifest.seed.json) so compose/run hit local images. A product needing
|
|
649
|
+
# more (e.g. a WHIP driver) pulls them in scripts/doc-guide/prepull-extra.sh.
|
|
650
|
+
- name: Pre-pull the media + studio images
|
|
651
|
+
run: |
|
|
652
|
+
set -euo pipefail
|
|
653
|
+
for img in "$(jq -r '.latest.media' manifest.seed.json)" "$(jq -r '.latest.studio' manifest.seed.json)"; do
|
|
654
|
+
echo "pre-pulling $img"
|
|
655
|
+
docker pull "$img"
|
|
656
|
+
done
|
|
657
|
+
if [ -x scripts/doc-guide/prepull-extra.sh ]; then
|
|
658
|
+
echo "running scripts/doc-guide/prepull-extra.sh"
|
|
659
|
+
./scripts/doc-guide/prepull-extra.sh
|
|
660
|
+
fi
|
|
661
|
+
- name: Install and build the workspaces the guides drive
|
|
662
|
+
run: |
|
|
663
|
+
nix develop .#build --command bash -c '
|
|
664
|
+
set -euo pipefail
|
|
665
|
+
find . -name node_modules -type d -prune -exec rm -rf {} + 2>/dev/null || true
|
|
666
|
+
bun install --frozen-lockfile
|
|
667
|
+
bun run build:no-lint
|
|
668
|
+
'
|
|
669
|
+
|
|
670
|
+
# The engine tier: real instances, driven in a browser. Reported, not
|
|
671
|
+
# gating (see the deterministic step). A non-zero exit is forgiven only
|
|
672
|
+
# when the tier's own results say a teardown hook timed out after every
|
|
673
|
+
# guide's test passed (doc-guide/engine-verdict.ts), as regen-manual.sh
|
|
674
|
+
# forgives it. A product with no docs:guides:engine script has no engine
|
|
675
|
+
# tier. Runs on the workspaces the step before it installed and built.
|
|
676
|
+
- name: Run the engine-tier guides (reported, not gating)
|
|
677
|
+
id: engine
|
|
678
|
+
continue-on-error: true
|
|
679
|
+
env:
|
|
680
|
+
NORSK_CTL_BINARY: ${{ runner.temp }}/norsk-ctl
|
|
681
|
+
NORSK_LICENSE_FILE: ${{ runner.temp }}/norsk-license.json
|
|
682
|
+
# This runner launches Studio as a HOST sibling (DooD), so the harness
|
|
683
|
+
# reaches host-published ports via the host-gateway alias, not
|
|
684
|
+
# localhost — same as the integration suite.
|
|
685
|
+
NORSK_TEST_HOST: host.docker.internal
|
|
686
|
+
# Reach launched instances over norsk-net by service DNS (as the
|
|
687
|
+
# integration tier already does) AND, on a ctl that supports it, launch
|
|
688
|
+
# them binding no host ports — so concurrent doc-guide instances can't
|
|
689
|
+
# collide on the product's deterministic host-port bands.
|
|
690
|
+
NORSK_TEST_NET: direct
|
|
691
|
+
run: |
|
|
692
|
+
nix develop .#build --command bash -c '
|
|
693
|
+
set -euo pipefail
|
|
694
|
+
if ! jq -e ".scripts[\"docs:guides:engine\"]" package.json >/dev/null; then
|
|
695
|
+
echo "no docs:guides:engine script: this product has no engine tier"
|
|
696
|
+
exit 0
|
|
697
|
+
fi
|
|
698
|
+
export DOCS_GENERATE=1
|
|
699
|
+
log="$(mktemp)"
|
|
700
|
+
if bash scripts/with-display.sh bun run docs:guides:engine >"$log" 2>&1; then
|
|
701
|
+
cat "$log"
|
|
702
|
+
else
|
|
703
|
+
cat "$log"
|
|
704
|
+
verdict="$(bun -e "console.log(Bun.resolveSync(\"@norskvideo/ctl-dev-kit/doc-guide/engine-verdict\", process.cwd()))")"
|
|
705
|
+
bun "$verdict" "$log"
|
|
706
|
+
fi
|
|
707
|
+
'
|
|
708
|
+
|
|
709
|
+
# Said three ways, so a failure is never silent: a warning on the run, a
|
|
710
|
+
# line in its summary, and the cell's detail (the notify job).
|
|
711
|
+
- name: Report the engine tier (not gating)
|
|
712
|
+
if: ${{ !cancelled() }}
|
|
713
|
+
env:
|
|
714
|
+
OUTCOME: ${{ steps.engine.outcome }}
|
|
715
|
+
run: |
|
|
716
|
+
case "$OUTCOME" in
|
|
717
|
+
success) echo "engine-tier guides passed (or this product has none)" ;;
|
|
718
|
+
failure)
|
|
719
|
+
echo "::warning title=engine-tier guides failed (not gating)::the engine tier does not gate until norsk-ctl task 57 is fixed. See the engine step's log."
|
|
720
|
+
printf '### Engine-tier guides: failed (not gating)\n\nThe deterministic guide tiers gate; the engine tier only reports until its container networking is fixed (norsk-ctl task 57). See the step log.\n' >> "$GITHUB_STEP_SUMMARY" ;;
|
|
721
|
+
*) echo "engine tier: ${OUTCOME:-not run}" ;;
|
|
722
|
+
esac
|
|
723
|
+
|
|
724
|
+
- name: Reap the containers this job launched
|
|
725
|
+
# Whatever the tests did. A teardown cut short by a test timeout leaves
|
|
726
|
+
# its instance running on the shared Docker daemon (see JOB_REAP_STEP in
|
|
727
|
+
# conventions/workflow-shell.ts); everything this job launched mounts its
|
|
728
|
+
# own test-temp, so that is what goes.
|
|
729
|
+
if: always()
|
|
730
|
+
run: |
|
|
731
|
+
base="${NORSK_CTL_TEST_TMP:-}"
|
|
732
|
+
tt="$GITHUB_WORKSPACE/test-temp"
|
|
733
|
+
s=""
|
|
734
|
+
for c in $(docker ps -aq); do
|
|
735
|
+
src="$(docker inspect -f '{{range .Mounts}}{{println .Source}}{{end}}' "$c" 2>/dev/null)" || continue
|
|
736
|
+
if { [ -n "$base" ] && grep -q "^$base/" <<<"$src"; } || grep -q "^$tt/" <<<"$src"; then s="$s $c"; fi
|
|
737
|
+
done
|
|
738
|
+
if [ -n "$s" ]; then
|
|
739
|
+
echo "::warning::removing containers this job left running:$s"
|
|
740
|
+
docker rm -f $s
|
|
741
|
+
fi
|
|
742
|
+
|
|
489
743
|
# Combine the per-arch images into a multi-arch manifest and write the two
|
|
490
744
|
# published tags. Skipped for a build-only product (empty PUBLISH_IMAGE) and on
|
|
491
745
|
# a dry run.
|
|
@@ -733,7 +987,15 @@ jobs:
|
|
|
733
987
|
# the pony/emoji from the dispatched result. always() so a red or manual run
|
|
734
988
|
# still reports.
|
|
735
989
|
notify:
|
|
736
|
-
needs: [
|
|
990
|
+
needs: [
|
|
991
|
+
label,
|
|
992
|
+
build,
|
|
993
|
+
gate-checks,
|
|
994
|
+
gate-guides,
|
|
995
|
+
guides-engine,
|
|
996
|
+
publish,
|
|
997
|
+
record
|
|
998
|
+
]
|
|
737
999
|
if: ${{ !cancelled() && github.event_name != 'pull_request' }}
|
|
738
1000
|
runs-on: x64
|
|
739
1001
|
steps:
|
|
@@ -796,9 +1058,14 @@ jobs:
|
|
|
796
1058
|
- uses: actions/checkout@v5
|
|
797
1059
|
with:
|
|
798
1060
|
clean: true
|
|
1061
|
+
# guides-engine reports in the detail below and never sets the state: a
|
|
1062
|
+
# failed engine tier is not a failed build, nor is one cancelled in its
|
|
1063
|
+
# concurrency queue.
|
|
799
1064
|
- id: meta
|
|
1065
|
+
env:
|
|
1066
|
+
NEEDS: ${{ toJSON(needs) }}
|
|
800
1067
|
run: |
|
|
801
|
-
if
|
|
1068
|
+
if jq -e 'del(.["guides-engine"]) | [.[].result] | any(. == "failure" or . == "cancelled")' <<<"$NEEDS" >/dev/null; then
|
|
802
1069
|
echo "state=failure" >> "$GITHUB_OUTPUT"
|
|
803
1070
|
else
|
|
804
1071
|
echo "state=success" >> "$GITHUB_OUTPUT"
|
|
@@ -811,7 +1078,8 @@ jobs:
|
|
|
811
1078
|
status: ${{ steps.meta.outputs.state }}
|
|
812
1079
|
# Only where something is published: on a build-only repo the gate has
|
|
813
1080
|
# nothing to gate, and saying so on every build is noise.
|
|
814
|
-
|
|
1081
|
+
# And when the engine-tier guides failed: they report, never gate.
|
|
1082
|
+
detail: "${{ needs.label.outputs.publish == 'true' && needs.gate-guides.outputs.guides == 'skip' && format('guides not gating: {0}', needs.gate-guides.outputs.reason) || needs.guides-engine.outputs.result == 'failure' && 'engine-tier guides failed (not gating; norsk-ctl task 57)' || '' }}"
|
|
815
1083
|
- name: Reap the containers this job launched
|
|
816
1084
|
# Whatever the tests did. A teardown cut short by a test timeout leaves
|
|
817
1085
|
# its instance running on the shared Docker daemon (see JOB_REAP_STEP in
|