@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
package/build/flake.nix
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
# integration) uses, so image builds never depend on the
|
|
14
14
|
# ctl binary channel.
|
|
15
15
|
#
|
|
16
|
-
# The norsk-ctl pin below is bumped nightly by
|
|
16
|
+
# The norsk-ctl pin below is bumped nightly by bump.yml; by hand with
|
|
17
17
|
# packages/dev-kit/build/refresh-ctl-pin.sh (writes ctlVersion + hashes)
|
|
18
18
|
{
|
|
19
19
|
description = "norsk-ctl product build + dev shell";
|
package/build/flake.sdk-app.nix
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
# an sdk-app's dev loop is its own SDK app plus a Norsk engine, not a
|
|
11
11
|
# ctl-launched product container. It still PINS one: `ctlVersion` below is the
|
|
12
12
|
# norsk-ctl the product's CI tiers (smoke) launch it with, read from here so a
|
|
13
|
-
# result belongs to a commit, and moved nightly by
|
|
13
|
+
# result belongs to a commit, and moved nightly by bump.yml. Decision: bun for tooling (install / build /
|
|
14
14
|
# test), Node for the shipped runtime (the SDK's blessed runtime).
|
|
15
15
|
#
|
|
16
16
|
# nixpkgs is pinned to the same rev the Studio flake uses, so bun matches the
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
|
|
30
30
|
outputs = { self, nixpkgs, nixpkgs-biome }:
|
|
31
31
|
let
|
|
32
|
-
# The norsk-ctl CI runs (see above);
|
|
32
|
+
# The norsk-ctl CI runs (see above); bump.yml moves it to `latest`.
|
|
33
33
|
ctlVersion = "0.1.0-2026-09-15-81bbde8";
|
|
34
34
|
systems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ];
|
|
35
35
|
forAllSystems = f: builtins.listToAttrs (map (s: { name = s; value = f s; }) systems);
|
package/build/refresh-ctl-pin.sh
CHANGED
|
@@ -36,4 +36,4 @@ done
|
|
|
36
36
|
|
|
37
37
|
perl -0pi -e "s{(ctlVersion = \")[^\"]*(\")}{\${1}$ver\${2}}" "$flake"
|
|
38
38
|
echo "updated $flake"
|
|
39
|
-
echo "this is the starter's pin; each product's own flake.nix is bumped nightly by
|
|
39
|
+
echo "this is the starter's pin; each product's own flake.nix is bumped nightly by bump.yml"
|
|
@@ -79,17 +79,26 @@
|
|
|
79
79
|
(`norsk-ctl serve`) + `norsk-ctl` on PATH (the nix shell) + `NORSK_LICENSE_FILE`.
|
|
80
80
|
Anything the daemon can't see for you — a stored template, a rebuilt image — is
|
|
81
81
|
stale until this loop re-runs; don't debug a ghost.
|
|
82
|
-
- **The shared CI
|
|
83
|
-
|
|
82
|
+
- **The shared CI workflows are single-sourced too.** `.github/workflows/bump.yml`
|
|
83
|
+
and the rest are copied verbatim from `@norskvideo/ctl-dev-kit` (`conventions/`),
|
|
84
84
|
same as this core block and `nix/ctl-shell.nix`; `check:drift` fails CI if a copy
|
|
85
85
|
diverges. Only the `product:` key is per-repo. Never hand-edit the copy — edit
|
|
86
86
|
the dev-kit source and re-sync.
|
|
87
87
|
- **CI runs the norsk-ctl this repo pins** (`flake.nix` `ctlVersion`), never S3's
|
|
88
|
-
moving `latest`; only a `ctl-candidate` run tests a channel (`rc`).
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
88
|
+
moving `latest`; only a `ctl-candidate` run tests a channel (`rc`). One nightly
|
|
89
|
+
bump, `bump.yml`, moves the dev-kit, the ctl-* and moq-* packages, and the
|
|
90
|
+
engine/Studio/norsk-ctl pins together, and never pushes to main: it opens one PR
|
|
91
|
+
on `bump/nightly`, and `bot-prs` merges it once checks, smoke, integration and
|
|
92
|
+
the guides gate (build-image) are green on it. A red bump PR is the alarm for a
|
|
93
|
+
bad upstream change, and names what moved; main stays green. To land the rest
|
|
94
|
+
without a bad component: `gh workflow run bump.yml -f components=<the others>`
|
|
95
|
+
(`dev-kit`, `ctl-packages`, `pins`).
|
|
96
|
+
- **Flaky checks stay out of gates, never out of sight.** The guides gate runs
|
|
97
|
+
the deterministic tiers; the engine tier runs beside it and reports (summary,
|
|
98
|
+
warning, cell detail) without gating until norsk-ctl task 57 is fixed. A
|
|
99
|
+
known-flaky test goes in `.github/quarantine.json` with an owner, a date and
|
|
100
|
+
an exit condition: `checks` leaves it out of the gating run, then runs it on
|
|
101
|
+
its own and reports it. Fix it and take it out; don't let an entry age.
|
|
93
102
|
- **A config-driven workflow needs a parity-checked invariant contract.** If this
|
|
94
103
|
product emits its Studio graph dynamically from config, its load-bearing rules
|
|
95
104
|
are documented as stable-ID statements (`INV-<AREA>-<NNN>`) in an
|
package/conventions/bot-prs.yml
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
# Land the
|
|
1
|
+
# Land the nightly bump's green PR; put a red one on the product dashboard.
|
|
2
2
|
#
|
|
3
|
-
#
|
|
4
|
-
# the norsk-ctl pin
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
3
|
+
# One bot keeps a product fresh: bump.yml, which moves the dev-kit, the ctl-* /
|
|
4
|
+
# moq-* npm family, and the Norsk nightly pair and norsk-ctl pin in one run and
|
|
5
|
+
# opens one PR on bump/nightly (ci-workflows docs/dashboard/ci-fragility.md,
|
|
6
|
+
# proposal B2). It never pushes to main (proposal B): its own gate is lint,
|
|
7
|
+
# typecheck and unit tests, and the tiers that break on an upstream change --
|
|
8
|
+
# smoke, integration and the guides that gate build-image's publish -- run on
|
|
9
|
+
# the PR. This workflow is what lands it. Until bump.yml, three bots did that
|
|
10
|
+
# job, each with its own branch (upgrade-latest/*, sync-dev-kit/*,
|
|
11
|
+
# chore/sync-ctl-packages); those are still triaged, so their PRs still in
|
|
12
|
+
# flight when a product moves to bump.yml land or are reported like any other.
|
|
13
|
+
# Nothing used to act on bot PRs: green
|
|
11
14
|
# ones sat unmerged (studio's ctl-sdk was twenty releases behind on 2026-10-03,
|
|
12
15
|
# its PR open since 09-03) and red ones were invisible, because the bot that
|
|
13
16
|
# opened them reports success. With a product count that only goes up, "someone
|
|
@@ -23,8 +26,9 @@
|
|
|
23
26
|
# push alone cannot gate, and still runs on main after the merge. "Nothing
|
|
24
27
|
# failed yet" is not enough: a slow tier that has not started has no check
|
|
25
28
|
# run at all, so bot-prs used to merge before integration had begun;
|
|
26
|
-
# - it conflicts with main -> re-run
|
|
27
|
-
#
|
|
29
|
+
# - it conflicts with main -> re-run bump.yml, ONCE per head, so it rebuilds
|
|
30
|
+
# the night from today's main (see below). Asked FIRST, whatever its
|
|
31
|
+
# checks say: the rebuild re-runs them all on the new base;
|
|
28
32
|
# - a check failed, it still conflicts after that rebuild, or it is a draft
|
|
29
33
|
# (the bot's own gate was red) -> list it, and report this repo's `bot-prs`
|
|
30
34
|
# cell RED on the product dashboard, which is the Discord message people
|
|
@@ -34,24 +38,30 @@
|
|
|
34
38
|
# workflow is broken, or does not run on PRs), so that is red too.
|
|
35
39
|
# The cell goes green again once nothing is red.
|
|
36
40
|
#
|
|
37
|
-
# Why the conflict rebuild: the three bots
|
|
38
|
-
# each
|
|
39
|
-
#
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
-
#
|
|
43
|
-
# from main when run:
|
|
44
|
-
# closes any older PR
|
|
41
|
+
# Why the conflict rebuild: when the three bots fired within minutes of each
|
|
42
|
+
# other, each writing package.json and bun.lock from main as it stood,
|
|
43
|
+
# whichever landed first left the others conflicting, and a bot rebuilt its
|
|
44
|
+
# branch only on its next DAILY run (2026-10-09: commentary, probe and reuters
|
|
45
|
+
# each had a sync-ctl-packages PR "needing a human" that a re-run fixed). One
|
|
46
|
+
# bump ends that, but a human's merge to main can still make bump/nightly
|
|
47
|
+
# conflict. bump.yml rebuilds from main when run: it force-pushes bump/nightly
|
|
48
|
+
# from today's main and closes any older bump PR, and an old bot's PR for each
|
|
49
|
+
# component it moved -- so it is the rebuild for those branches too, whose own
|
|
50
|
+
# workflows the move to bump.yml deleted. The marker comment caps it at one
|
|
45
51
|
# rebuild per head, so a rebuild that still conflicts goes to a human instead
|
|
46
52
|
# of looping every hour.
|
|
47
53
|
#
|
|
54
|
+
# It triages the bot PRs that target one base: main, unless a dispatch names
|
|
55
|
+
# another (`base`). A trial of a dev-kit change against a throwaway branch is
|
|
56
|
+
# triaged, rebuilt and merged there, never on main, and reports nothing.
|
|
57
|
+
#
|
|
48
58
|
# Runs when any gating tier finishes (the prompt path), hourly as a sweep,
|
|
49
59
|
# and on demand. No checkout: it only talks to the GitHub API, so it needs none
|
|
50
60
|
# of the runner-workspace steps the building workflows carry.
|
|
51
61
|
#
|
|
52
62
|
# Single-sourced in @norskvideo/ctl-dev-kit (conventions/bot-prs.yml) and copied
|
|
53
63
|
# verbatim into each product repo -- it reads its dashboard key from this repo's
|
|
54
|
-
#
|
|
64
|
+
# bump.yml rather than carrying one -- so sync-drift creates it where
|
|
55
65
|
# absent and check:drift fails a copy that diverges. Never hand-edit the copy.
|
|
56
66
|
name: bot-prs
|
|
57
67
|
|
|
@@ -62,6 +72,10 @@ on:
|
|
|
62
72
|
schedule:
|
|
63
73
|
- cron: "23 * * * *"
|
|
64
74
|
workflow_dispatch:
|
|
75
|
+
inputs:
|
|
76
|
+
base:
|
|
77
|
+
description: "Triage the bot PRs that target this branch. main, unless trialling on a throwaway branch."
|
|
78
|
+
default: main
|
|
65
79
|
|
|
66
80
|
permissions:
|
|
67
81
|
contents: read
|
|
@@ -96,6 +110,7 @@ jobs:
|
|
|
96
110
|
READ_TOKEN: ${{ github.token }}
|
|
97
111
|
WRITE_TOKEN: ${{ secrets.CI_DISPATCH_TOKEN }}
|
|
98
112
|
REPO: ${{ github.repository }}
|
|
113
|
+
BASE: ${{ inputs.base || 'main' }}
|
|
99
114
|
run: |
|
|
100
115
|
set -euo pipefail
|
|
101
116
|
api="https://api.github.com/repos/$REPO"
|
|
@@ -114,12 +129,14 @@ jobs:
|
|
|
114
129
|
exit 1
|
|
115
130
|
}
|
|
116
131
|
|
|
117
|
-
# The
|
|
132
|
+
# The bump owns bump/nightly (see bump.yml's PR step); the other three
|
|
133
|
+
# are the retired bots' branches, still landed while in flight.
|
|
118
134
|
pulls="$(gh_api "$api/pulls?state=open&per_page=100")"
|
|
119
135
|
expect "listing open PRs" 'type == "array"' "$pulls"
|
|
120
|
-
prs="$(jq -c --arg repo "$REPO" '
|
|
136
|
+
prs="$(jq -c --arg repo "$REPO" --arg base "$BASE" '
|
|
121
137
|
[.[] | select(.head.repo.full_name == $repo)
|
|
122
|
-
| select(.
|
|
138
|
+
| select(.base.ref == $base)
|
|
139
|
+
| select(.head.ref | test("^(bump/nightly$|upgrade-latest/|sync-dev-kit/|chore/sync-ctl-packages$)"))
|
|
123
140
|
| {number, ref: .head.ref, sha: .head.sha, draft, url: .html_url}]' <<<"$pulls")"
|
|
124
141
|
|
|
125
142
|
# Does this workflow (on stdin) run on pull_request? Only its `on:` block
|
|
@@ -204,12 +221,12 @@ jobs:
|
|
|
204
221
|
fi
|
|
205
222
|
}
|
|
206
223
|
|
|
207
|
-
# The workflow that
|
|
224
|
+
# The workflow that rebuilds each bot branch: bump.yml, for the retired
|
|
225
|
+
# bots' branches too (their workflows went when bump.yml arrived, and
|
|
226
|
+
# bump.yml supersedes their PRs for every component it moves).
|
|
208
227
|
producer() {
|
|
209
228
|
case "$1" in
|
|
210
|
-
upgrade-latest/*) echo
|
|
211
|
-
sync-dev-kit/*) echo sync-dev-kit.yml ;;
|
|
212
|
-
chore/sync-ctl-packages) echo sync-ctl-packages.yml ;;
|
|
229
|
+
bump/nightly | upgrade-latest/* | sync-dev-kit/* | chore/sync-ctl-packages) echo bump.yml ;;
|
|
213
230
|
*) echo "" ;;
|
|
214
231
|
esac
|
|
215
232
|
}
|
|
@@ -220,31 +237,14 @@ jobs:
|
|
|
220
237
|
while IFS= read -r pr; do
|
|
221
238
|
[ -n "$pr" ] || continue
|
|
222
239
|
n="$(jq -r .number <<<"$pr")"; ref="$(jq -r .ref <<<"$pr")"; sha="$(jq -r .sha <<<"$pr")"
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
# Every tier that runs on a bot PR must have PASSED on this head.
|
|
232
|
-
required="$(tiers_for "$sha")"
|
|
233
|
-
wf_runs="$(gh_api "$api/actions/runs?head_sha=$sha&per_page=100")"
|
|
234
|
-
expect "reading workflow runs for #$n" '.workflow_runs | type == "array"' "$wf_runs"
|
|
235
|
-
verdict="$(tier_verdict "$required" "$wf_runs" "$(date +%s)")"
|
|
236
|
-
case "$verdict" in
|
|
237
|
-
ok) ;;
|
|
238
|
-
red\ *) red+=("#$n $ref: ${verdict#red }"); continue ;;
|
|
239
|
-
rerun\ *)
|
|
240
|
-
for id in ${verdict#rerun }; do
|
|
241
|
-
code="$(gh_actions -o /dev/null -w '%{http_code}' -X POST "$api/actions/runs/$id/rerun")"
|
|
242
|
-
echo "#$n $ref: a gating tier (run $id) was cancelled; re-running it (HTTP $code)"
|
|
243
|
-
done
|
|
244
|
-
continue ;;
|
|
245
|
-
*) echo "#$n $ref: ${verdict#wait }; next time"; continue ;;
|
|
246
|
-
esac
|
|
247
|
-
# GitHub computes mergeability lazily; ask until it has an answer.
|
|
240
|
+
# Mergeability FIRST, before the checks are judged. A rebuild re-runs
|
|
241
|
+
# every tier on today's base, so a conflicting PR is rebuilt whatever
|
|
242
|
+
# its checks say: relay #11 (2026-10-10) carried a smoke failure from
|
|
243
|
+
# before main moved, was reported red for it, and sat conflicting
|
|
244
|
+
# for 90 minutes with its rebuild never asked for. Only a PR that
|
|
245
|
+
# still conflicts after its rebuild, or merges cleanly with red
|
|
246
|
+
# checks, is red. GitHub computes mergeability lazily; ask until it
|
|
247
|
+
# has an answer.
|
|
248
248
|
mergeable=null
|
|
249
249
|
for _ in 1 2 3 4 5; do
|
|
250
250
|
mergeable="$(gh_api "$api/pulls/$n" | jq -r .mergeable)"
|
|
@@ -262,9 +262,9 @@ jobs:
|
|
|
262
262
|
elif jq -e --arg m "$marker" 'any(.[]; .body | contains($m))' >/dev/null <<<"$comments"; then
|
|
263
263
|
red+=("#$n $ref: still does not merge cleanly with main after $wf rebuilt it")
|
|
264
264
|
else
|
|
265
|
-
code="$(gh_actions -o /dev/null -w '%{http_code}' -X POST "$api/actions/workflows/$wf/dispatches" -d '{
|
|
265
|
+
code="$(gh_actions -o /dev/null -w '%{http_code}' -X POST "$api/actions/workflows/$wf/dispatches" -d "$(jq -nc --arg b "$BASE" '{ref:$b, inputs:{base:$b}}')")"
|
|
266
266
|
if [ "$code" = "204" ]; then
|
|
267
|
-
body="$marker -- it conflicted with
|
|
267
|
+
body="$marker -- it conflicted with $BASE, so $wf was re-run to rebuild the bump from today's $BASE. If the rebuilt branch conflicts too, this PR is reported for a human."
|
|
268
268
|
gh_write -o /dev/null -X POST "$api/issues/$n/comments" -d "$(jq -nc --arg b "$body" '{body:$b}')" || true
|
|
269
269
|
rebuilt+=("#$n")
|
|
270
270
|
echo "#$n $ref: conflicts with main; re-ran $wf to rebuild it"
|
|
@@ -274,6 +274,30 @@ jobs:
|
|
|
274
274
|
fi
|
|
275
275
|
continue
|
|
276
276
|
fi
|
|
277
|
+
runs="$(gh_api "$api/commits/$sha/check-runs?per_page=100")"
|
|
278
|
+
expect "reading checks for #$n" '.check_runs | type == "array"' "$runs"
|
|
279
|
+
failed="$(jq -r '[.check_runs[] | select(.conclusion | IN("failure","cancelled","timed_out","action_required","startup_failure")) | .name] | unique | join(", ")' <<<"$runs")"
|
|
280
|
+
pending="$(jq -r '[.check_runs[] | select(.status != "completed")] | length' <<<"$runs")"
|
|
281
|
+
passed="$(jq -r '[.check_runs[] | select(.conclusion == "success")] | length' <<<"$runs")"
|
|
282
|
+
if [ -n "$failed" ]; then red+=("#$n $ref: failing $failed"); continue; fi
|
|
283
|
+
if [ "$(jq -r .draft <<<"$pr")" = "true" ]; then red+=("#$n $ref: draft (its bot's gate was red)"); continue; fi
|
|
284
|
+
if [ "$pending" -gt 0 ] || [ "$passed" -eq 0 ]; then echo "#$n $ref: checks not finished; next time"; continue; fi
|
|
285
|
+
# Every tier that runs on a bot PR must have PASSED on this head.
|
|
286
|
+
required="$(tiers_for "$sha")"
|
|
287
|
+
wf_runs="$(gh_api "$api/actions/runs?head_sha=$sha&per_page=100")"
|
|
288
|
+
expect "reading workflow runs for #$n" '.workflow_runs | type == "array"' "$wf_runs"
|
|
289
|
+
verdict="$(tier_verdict "$required" "$wf_runs" "$(date +%s)")"
|
|
290
|
+
case "$verdict" in
|
|
291
|
+
ok) ;;
|
|
292
|
+
red\ *) red+=("#$n $ref: ${verdict#red }"); continue ;;
|
|
293
|
+
rerun\ *)
|
|
294
|
+
for id in ${verdict#rerun }; do
|
|
295
|
+
code="$(gh_actions -o /dev/null -w '%{http_code}' -X POST "$api/actions/runs/$id/rerun")"
|
|
296
|
+
echo "#$n $ref: a gating tier (run $id) was cancelled; re-running it (HTTP $code)"
|
|
297
|
+
done
|
|
298
|
+
continue ;;
|
|
299
|
+
*) echo "#$n $ref: ${verdict#wait }; next time"; continue ;;
|
|
300
|
+
esac
|
|
277
301
|
# sha pins the merge to what was checked: a bot force-pushing the
|
|
278
302
|
# branch meanwhile makes this refuse rather than land unchecked code.
|
|
279
303
|
res="$(gh_write -X PUT "$api/pulls/$n/merge" -d "$(jq -nc --arg sha "$sha" '{merge_method:"squash", sha:$sha}')")"
|
|
@@ -304,7 +328,8 @@ jobs:
|
|
|
304
328
|
|
|
305
329
|
- name: Report to the product dashboard
|
|
306
330
|
# Always: a triage that broke is exactly what nobody would otherwise see.
|
|
307
|
-
|
|
331
|
+
# But only for main: a trial against another base must not paint the board.
|
|
332
|
+
if: always() && (inputs.base || 'main') == 'main'
|
|
308
333
|
env:
|
|
309
334
|
TOKEN: ${{ secrets.CI_DISPATCH_TOKEN }}
|
|
310
335
|
READ_TOKEN: ${{ github.token }}
|
|
@@ -317,13 +342,18 @@ jobs:
|
|
|
317
342
|
REPO: ${{ github.repository }}
|
|
318
343
|
run: |
|
|
319
344
|
set -euo pipefail
|
|
320
|
-
# The dashboard key is the one
|
|
321
|
-
#
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
345
|
+
# The dashboard key is the one bump.yml reports under, read from it so
|
|
346
|
+
# this file can stay byte-identical across products; upgrade-latest.yml
|
|
347
|
+
# carried it before bump.yml did. -f: a 404 must not read as a key.
|
|
348
|
+
product=""
|
|
349
|
+
for wf in bump.yml upgrade-latest.yml; do
|
|
350
|
+
product="$(curl -fsS -H "Authorization: Bearer $READ_TOKEN" -H "Accept: application/vnd.github.raw" \
|
|
351
|
+
"https://api.github.com/repos/$REPO/contents/.github/workflows/$wf" 2>/dev/null \
|
|
352
|
+
| sed -nE 's/^[[:space:]]*product:[[:space:]]*([^[:space:]]+).*/\1/p' | head -1 || true)"
|
|
353
|
+
[ -z "$product" ] || break
|
|
354
|
+
done
|
|
325
355
|
if [ -z "$product" ] || [ "$product" = "__PRODUCT__" ]; then
|
|
326
|
-
echo "::warning::no product key in
|
|
356
|
+
echo "::warning::no product key in bump.yml; not reporting to the dashboard"
|
|
327
357
|
exit 0
|
|
328
358
|
fi
|
|
329
359
|
ts="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
@@ -164,15 +164,40 @@ jobs:
|
|
|
164
164
|
NORSK_LICENSE_V2: ${{ secrets.NORSK_LICENSE_V2 }}
|
|
165
165
|
run: printf '%s' "$NORSK_LICENSE_V2" > "$RUNNER_TEMP/norsk-license.json"
|
|
166
166
|
|
|
167
|
+
# Every norsk-ctl daemon starts the node MoQ relay (norsk-moq-relay) unless
|
|
168
|
+
# told not to, and nothing removes it: it is a node singleton, and the reap
|
|
169
|
+
# step only takes containers that mount this job's test-temp. So the first
|
|
170
|
+
# test daemon on a shared runner left one, and every later one adopted it
|
|
171
|
+
# (probe's smoke ran in relay mode on 2026-10-10; relay's smoke lost 4443
|
|
172
|
+
# to it). This job's daemons run without the node relay and discovery, so
|
|
173
|
+
# they neither start nor adopt one, and whatever is already on the host is
|
|
174
|
+
# named here. It reports and never fails: the leftover may be someone's.
|
|
175
|
+
- name: Isolate this job from node singletons other jobs left on this host
|
|
176
|
+
run: |
|
|
177
|
+
set -uo pipefail
|
|
178
|
+
echo "NORSK_CTL_MOQ_RELAY_ENABLE=0" >> "$GITHUB_ENV"
|
|
179
|
+
echo "NORSK_CTL_DISCOVERY_ENABLE=0" >> "$GITHUB_ENV"
|
|
180
|
+
left="$(docker ps -a --filter label=norsk-ctl.role --format '{{.Names}} {{.Label "norsk-ctl.role"}} {{.Status}} (created {{.CreatedAt}})' 2>/dev/null || true)"
|
|
181
|
+
if [ -z "$left" ]; then echo "no norsk-ctl containers on this host"; exit 0; fi
|
|
182
|
+
echo "norsk-ctl containers already on this host (other jobs' running instances are expected):"
|
|
183
|
+
echo "$left"
|
|
184
|
+
while IFS= read -r line; do
|
|
185
|
+
role="$(awk '{print $2}' <<<"$line")"
|
|
186
|
+
case "$role" in
|
|
187
|
+
moq-relay|discovery)
|
|
188
|
+
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." ;;
|
|
189
|
+
esac
|
|
190
|
+
done <<<"$left"
|
|
191
|
+
|
|
167
192
|
# The norsk-ctl this commit pins (flake.nix ctlVersion, moved by
|
|
168
|
-
#
|
|
193
|
+
# bump.yml's nightly PR), never S3's moving `latest`: a guide run is then
|
|
169
194
|
# a real repeat, and a red belongs to a commit (ci-fragility proposal A).
|
|
170
195
|
- name: Download the pinned norsk-ctl binary (flake.nix ctlVersion)
|
|
171
196
|
run: |
|
|
172
197
|
set -euo pipefail
|
|
173
198
|
S3="https://s3.eu-west-1.amazonaws.com/norsk.video/norsk-ctl"
|
|
174
199
|
ver="$( { sed -nE 's/^[[:space:]]*ctlVersion = "([^"]+)";.*/\1/p' flake.nix 2>/dev/null || true; } | head -1)"
|
|
175
|
-
[ -n "$ver" ] || { echo "::error::flake.nix pins no ctlVersion. CI runs the norsk-ctl this commit pins (
|
|
200
|
+
[ -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; }
|
|
176
201
|
echo "norsk-ctl pinned by flake.nix ctlVersion -> $ver"
|
|
177
202
|
curl -fsSL --retry 3 --retry-all-errors --retry-delay 2 "$S3/$ver/norsk-ctl-$ver-linux-x64" -o "$RUNNER_TEMP/norsk-ctl"
|
|
178
203
|
chmod +x "$RUNNER_TEMP/norsk-ctl"
|