@norskvideo/ctl-dev-kit 0.2.27 → 0.2.28

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.
@@ -1,5 +1,8 @@
1
1
  # Keep the product fresh against the Norsk "nightly" channel (RFC 0001 —
2
- # product version freshness). Version discovery anchors on manifest.seed.json
2
+ # product version freshness). The {engine, studio image} pair comes from the
3
+ # nightly entry of norsk-studio's channel manifest -- the pair studio TESTED
4
+ # together -- never from the two channels separately. Version discovery of the
5
+ # product's current pins anchors on manifest.seed.json
3
6
  # (the machine-writable source of truth for the launched {media, studio} image
4
7
  # pair — every product repo has one). Any npm pins on the same version string
5
8
  # (media build libraries, the @norskvideo/norsk-studio* family in whichever
@@ -119,22 +122,41 @@ jobs:
119
122
  # product, even when nothing changed — pure dashboard noise.
120
123
  parts=()
121
124
 
125
+ # ONE TESTED PAIR, NOT TWO CHANNELS. The engine and the studio image are
126
+ # taken together from the nightly entry of norsk-studio's channel
127
+ # manifest: the pair studio's publish built and tested against each
128
+ # other (scripts/write-channel-manifest.sh in norsk-studio). Resolving
129
+ # them apart -- the engine off norsk-sdk's nightly dist-tag, the image
130
+ # off Docker Hub's nightly tag -- produced pairs nobody had tested
131
+ # whenever one channel moved without the other. On 2026-10-04 studio's
132
+ # nightly had been held by its test gate since 09-28 while the engine
133
+ # walked on to 10-02, a breaking SDK change landed in between, and
134
+ # commentary's audio mix produced nothing on the old-studio /
135
+ # new-engine pair it was pinned to (norsk task 112).
136
+ #
137
+ # Consequence, deliberately: when studio's nightly stalls, the engine
138
+ # stalls with it (or steps BACK to the pair), rather than running ahead
139
+ # untested. Unblocking studio's nightly is how products move forward.
140
+ #
141
+ # Everything is resolved and checked BEFORE anything is rewritten, so a
142
+ # refusal never leaves the engine moved and studio not.
143
+ manifest_url="https://s3.eu-west-1.amazonaws.com/norsk.video/channel-manifest.json"
144
+ pair="$(curl -fsSL --retry 3 --retry-all-errors --retry-delay 2 "$manifest_url" | jq -c '.channels.nightly // empty')"
145
+ [ -n "$pair" ] || { echo "::error::no nightly pair in $manifest_url"; exit 1; }
146
+ tag_re='^[0-9]+\.[0-9]+\.[0-9]+-[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9a-f]+$'
147
+ new_media="$(jq -r '(.norsk // "") | split(":") | .[1] // ""' <<<"$pair")"
148
+ new_studio_img="$(jq -r '(.studio // "") | split(":") | .[1] // ""' <<<"$pair")"
149
+ printf '%s' "$new_media" | grep -qE "$tag_re" || { echo "::error::the manifest's nightly norsk image is not a dated nightly tag: $pair"; exit 1; }
150
+ printf '%s' "$new_studio_img" | grep -qE "$tag_re" || { echo "::error::the manifest's nightly studio image is not a dated nightly tag: $pair"; exit 1; }
151
+ echo "nightly pair: norsk $new_media + studio $new_studio_img"
152
+
122
153
  # MEDIA: the npm packages and the launched image are built from the SAME
123
- # commit (their hash suffixes match), so one version string drives the
124
- # whole family. norsk-sdk is the family reference; read its nightly
125
- # dist-tag off the registry (curl+jq — the runner has no npm) and rewrite
126
- # every occurrence (overrides, sub-package deps, manifest image ref,
127
- # version-pin test) EXCEPT the lockfile, which bun install regenerates.
154
+ # commit (their hash suffixes match), so the pair's engine version drives
155
+ # the whole family: overrides, sub-package deps, the manifest image ref,
156
+ # the version-pin test -- every occurrence EXCEPT the lockfile, which bun
157
+ # install regenerates.
128
158
  old_media="$(jq -r '.latest.media | split(":")[1]' manifest.seed.json)"
129
- new_media="$(curl -fsSL --retry 3 --retry-all-errors --retry-delay 2 "$reg/@norskvideo%2Fnorsk-sdk" | jq -r '."dist-tags".nightly')"
130
- [ -n "$new_media" ] && [ "$new_media" != "null" ] || { echo "could not resolve norsk-sdk nightly"; exit 1; }
131
- echo "media: $old_media -> $new_media"
132
- if [ "$old_media" != "$new_media" ]; then
133
- git grep -lF "$old_media" -- . ':!bun.lock' | xargs -r sed -i "s|$old_media|$new_media|g"
134
- changed=1
135
- parts+=("media \`$new_media\`")
136
- fi
137
- # The rewrite above is a LITERAL replace of the value found in
159
+ # The rewrite below is a LITERAL replace of the value found in
138
160
  # manifest.seed.json, so it only ever touches pins that already equal the
139
161
  # seed. A pin that has drifted off it -- a hand edit, a merge, a
140
162
  # half-finished run -- is invisible to this bump and to every later one,
@@ -148,9 +170,10 @@ jobs:
148
170
  # Not auto-rewritten: a pin held back on purpose is someone's decision,
149
171
  # and silently moving it would be the same class of surprise in the
150
172
  # other direction. Fail, name them, and let a human fix the pin or the
151
- # intent.
173
+ # intent. (Checked against the SEED, the family's current value: after
174
+ # the rewrite every seed-equal pin reads the pair's engine.)
152
175
  stranded="$(git grep -hoE '"@norskvideo/norsk-(api|sdk|auto-manager|manager-sdk)"[[:space:]]*:[[:space:]]*"[0-9]+\.[0-9]+\.[0-9]+-[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9a-f]+"' -- '*package.json' \
153
- | sed -E 's/.*"([^"]+)"$/\1/' | sort -u | grep -vxF "$new_media" || true)"
176
+ | sed -E 's/.*"([^"]+)"$/\1/' | sort -u | grep -vxF "$old_media" | grep -vxF "$new_media" || true)"
154
177
  if [ -n "$stranded" ]; then
155
178
  echo "::error::npm norsk pins are stranded off the nightly channel: $(printf '%s' "$stranded" | tr '\n' ' ')"
156
179
  echo "The media family (api, sdk, auto-manager, manager-sdk) and the launched image are built from the same"
@@ -159,76 +182,73 @@ jobs:
159
182
  exit 1
160
183
  fi
161
184
 
162
- # STUDIO: unlike media, Norsk builds the studio npm packages and the
163
- # studio Docker image from DIFFERENT commits — their hash suffixes never
164
- # match — so one version string can't serve both: pinning the launched
165
- # image to the npm nightly yields a tag that was never pushed (integration
166
- # then dies on "manifest unknown"). Resolve the two apart: build libraries
167
- # from npm, the launched image from Docker Hub's newest published tag ON
168
- # THE SAME version line (so it's pullable AND matches the libs'
169
- # major.minor.patch). Rewrite the image ref FIRST, then the bare library
170
- # version — so even when the two happen to coincide, the image ref is
171
- # already moved and the bare-version rewrite only touches the npm pins
172
- # (no dependence on which files hold each — some products have no
173
- # version-pin test).
185
+ # STUDIO: the studio npm packages and the studio image are built from
186
+ # DIFFERENT commits -- their hash suffixes never match -- so the image
187
+ # comes from the pair and the build libraries from npm's nightly. The
188
+ # libraries must belong to the same pair: their norsk-sdk dependency has
189
+ # to name the pair's engine, and their version line the image's. A
190
+ # mismatch means the channels are inconsistent (studio published npm
191
+ # without updating the manifest, or the reverse); refuse rather than pin
192
+ # a combination nobody tested. (Studio's publish installs the SDK it
193
+ # builds against as a DEV dependency -- `npm install --save-dev
194
+ # @norskvideo/norsk-sdk@<channel>` -- so that is where the pairing lives.)
174
195
  #
175
196
  # The old npm pin is discovered from the repo's REAL studio deps: any
176
197
  # exact nightly on a @norskvideo/norsk-studio* package in a tracked
177
- # package.json (deps, devDeps, or overrides — wherever it lives). A
178
- # repo with none skips the lib rewrite; nothing forces phantom
179
- # `overrides` entries just to give this script an anchor. Distinct
180
- # pins disagreeing across files abort rather than half-rewrite.
198
+ # package.json (deps, devDeps, or overrides -- wherever it lives). A
199
+ # repo with none skips the lib entirely. Distinct pins disagreeing
200
+ # across files abort rather than half-rewrite.
181
201
  old_studio_lib="$(git grep -hoE '"@norskvideo/norsk-studio[^"]*"[[:space:]]*:[[:space:]]*"[0-9]+\.[0-9]+\.[0-9]+-[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9a-f]+"' -- '*package.json' \
182
202
  | sed -E 's/.*"([^"]+)"$/\1/' | sort -u || true)"
183
203
  if [ "$(printf '%s' "$old_studio_lib" | grep -c .)" -gt 1 ]; then
184
204
  echo "studio npm pins disagree across package.json files:"; echo "$old_studio_lib"; exit 1
185
205
  fi
186
- new_studio_lib="$(curl -fsSL --retry 3 --retry-all-errors --retry-delay 2 "$reg/@norskvideo%2Fnorsk-studio" | jq -r '."dist-tags".nightly')"
187
- [ -n "$new_studio_lib" ] && [ "$new_studio_lib" != "null" ] || { echo "could not resolve norsk-studio npm nightly"; exit 1; }
188
- studio_line="${new_studio_lib%%-*}" # e.g. 1.28.0 — keep the image on this line
189
206
  old_studio_img="$(jq -r '.latest.studio | split(":")[1]' manifest.seed.json)"
190
- # FOLLOW THE NIGHTLY CHANNEL — do not pick the newest tag NAME on the
191
- # line. Newest-name selects on publish recency, so any build landing a
192
- # <line>-<date>-<sha> tag wins, including one off a feature branch. Not
193
- # hypothetical: a norsk-studio `reasoning` build took both the nightly
194
- # tag and the newest-name slot on 2026-08-25, this job pinned it at
195
- # 04:29, and every signed probe workflow was refused at launch that
196
- # morning because the fix on main was not in it. norsk-studio now
197
- # refuses to publish `nightly` from anything but main, so the channel is
198
- # the trustworthy signal — and this follows it rather than guessing.
199
- #
200
- # Resolve nightly's digest, then name the version-specific tag carrying
201
- # the same digest. The moving tag is not pinnable itself: it moves under
202
- # you, and a launch has to record the exact image it ran.
203
- hub="https://hub.docker.com/v2/repositories/norskvideo/norsk-studio"
204
- nightly_digest="$(curl -fsSL --retry 3 --retry-all-errors --retry-delay 2 "$hub/tags/nightly" | jq -r '.digest // empty')"
205
- [ -n "$nightly_digest" ] || { echo "could not resolve the norsk-studio nightly tag's digest"; exit 1; }
206
- new_studio_img="$(curl -fsSL --retry 3 --retry-all-errors --retry-delay 2 "$hub/tags?page_size=100&ordering=last_updated" \
207
- | jq -r --arg d "$nightly_digest" '.results[] | select(.digest == $d) | .name' \
208
- | grep -E "^${studio_line}-[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9a-f]+$" | head -1 || true)"
209
- [ -n "$new_studio_img" ] || {
210
- echo "norsk-studio nightly ($nightly_digest) has no version-specific tag on the $studio_line line."
211
- echo "Either the npm and docker version lines have diverged, or nightly has moved to a"
212
- echo "different line than the npm nightly ($new_studio_lib). Not guessing — fix the channel."
213
- exit 1
214
- }
215
- echo "studio lib: ${old_studio_lib:-<no npm pins>} -> $new_studio_lib"
207
+ new_studio_lib=""
208
+ if [ -n "$old_studio_lib" ]; then
209
+ studio_doc="$(curl -fsSL --retry 3 --retry-all-errors --retry-delay 2 "$reg/@norskvideo%2Fnorsk-studio")"
210
+ new_studio_lib="$(jq -r '."dist-tags".nightly // empty' <<<"$studio_doc")"
211
+ [ -n "$new_studio_lib" ] || { echo "could not resolve norsk-studio npm nightly"; exit 1; }
212
+ lib_sdk="$(jq -r --arg v "$new_studio_lib" \
213
+ '.versions[$v] | ((.devDependencies // {}) + (.peerDependencies // {}) + (.dependencies // {}))["@norskvideo/norsk-sdk"] // empty' <<<"$studio_doc")"
214
+ lib_sdk="${lib_sdk#^}"; lib_sdk="${lib_sdk#~}"
215
+ if [ "$lib_sdk" != "$new_media" ]; then
216
+ echo "::error::norsk-studio npm nightly $new_studio_lib was built against norsk-sdk ${lib_sdk:-<none declared>}, not the tested pair's engine $new_media."
217
+ echo "The npm channel and the channel manifest disagree; not pinning a combination nobody tested."
218
+ exit 1
219
+ fi
220
+ if [ "${new_studio_lib%%-*}" != "${new_studio_img%%-*}" ]; then
221
+ echo "::error::norsk-studio npm nightly $new_studio_lib is on a different version line from the pair's image $new_studio_img."
222
+ exit 1
223
+ fi
224
+ fi
225
+
226
+ echo "media: $old_media -> $new_media"
227
+ echo "studio lib: ${old_studio_lib:-<no npm pins>} -> ${new_studio_lib:-<unchanged>}"
216
228
  echo "studio image: $old_studio_img -> $new_studio_img"
229
+ if [ "$old_media" != "$new_media" ]; then
230
+ git grep -lF "$old_media" -- . ':!bun.lock' | xargs -r sed -i "s|$old_media|$new_media|g"
231
+ changed=1
232
+ parts+=("media \`$new_media\`")
233
+ fi
234
+ # Rewrite the image ref FIRST, then the bare library version -- so even
235
+ # when the two coincide, the image ref has already moved and the
236
+ # bare-version rewrite only touches the npm pins.
217
237
  if [ "$old_studio_img" != "$new_studio_img" ]; then
218
238
  git grep -lF "norskvideo/norsk-studio:$old_studio_img" -- . ':!bun.lock' \
219
239
  | xargs -r sed -i "s|norskvideo/norsk-studio:$old_studio_img|norskvideo/norsk-studio:$new_studio_img|g"
220
240
  changed=1
221
241
  fi
222
- if [ -n "$old_studio_lib" ] && [ "$old_studio_lib" != "$new_studio_lib" ]; then
242
+ if [ -n "$new_studio_lib" ] && [ "$old_studio_lib" != "$new_studio_lib" ]; then
223
243
  git grep -lF "$old_studio_lib" -- . ':!bun.lock' | xargs -r sed -i "s|$old_studio_lib|$new_studio_lib|g"
224
244
  changed=1
225
245
  fi
226
- # One studio entry, not two — the launched image tag is the runtime-
246
+ # One studio entry, not two -- the launched image tag is the runtime-
227
247
  # relevant one; only fall back to the lib tag if the image held but the
228
- # library advanced (npm nightly moved with no new image on the line yet).
248
+ # library advanced.
229
249
  if [ "$old_studio_img" != "$new_studio_img" ]; then
230
250
  parts+=("studio \`$new_studio_img\`")
231
- elif [ -n "$old_studio_lib" ] && [ "$old_studio_lib" != "$new_studio_lib" ]; then
251
+ elif [ -n "$new_studio_lib" ] && [ "$old_studio_lib" != "$new_studio_lib" ]; then
232
252
  parts+=("studio-lib \`$new_studio_lib\`")
233
253
  fi
234
254
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@norskvideo/ctl-dev-kit",
3
- "version": "0.2.27",
3
+ "version": "0.2.28",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./create-product": "./create-product/create-product.ts",