@norskvideo/ctl-dev-kit 0.2.73 → 0.2.75

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.
@@ -304,7 +304,9 @@ jobs:
304
304
  # @norskvideo/norsk-studio* family in whichever tracked package.json
305
305
  # depends on them) move in lockstep via a global string-replace of old->new
306
306
  # across every tracked file -- which also updates the version-pin guard
307
- # test. A repo with no studio npm deps needs no `overrides` block at all.
307
+ # test -- except docs/internal/, whose records name the version something
308
+ # happened on and must keep saying so (#642). A repo with no studio npm
309
+ # deps needs no `overrides` block at all.
308
310
  #
309
311
  # A REFUSAL (the channels disagree, a pin is stranded, there is no
310
312
  # ctlVersion, a channel cannot be read) refuses the pins alone: the step
@@ -432,7 +434,7 @@ jobs:
432
434
  echo "studio lib: ${old_studio_lib:-<no npm pins>} -> ${new_studio_lib:-<unchanged>}"
433
435
  echo "studio image: $old_studio_img -> $new_studio_img"
434
436
  if [ "$old_media" != "$new_media" ]; then
435
- git grep -lF "$old_media" -- . ':!bun.lock' | xargs -r sed -i "s|$old_media|$new_media|g"
437
+ git grep -lF "$old_media" -- . ':!bun.lock' ':!docs/internal' | xargs -r sed -i "s|$old_media|$new_media|g"
436
438
  changed=1
437
439
  parts+=("media \`$new_media\`")
438
440
  moved+=("- media: $old_media -> $new_media")
@@ -441,7 +443,7 @@ jobs:
441
443
  # when the two coincide, the image ref has already moved and the
442
444
  # bare-version rewrite only touches the npm pins.
443
445
  if [ "$old_studio_img" != "$new_studio_img" ]; then
444
- git grep -lF "norskvideo/norsk-studio:$old_studio_img" -- . ':!bun.lock' \
446
+ git grep -lF "norskvideo/norsk-studio:$old_studio_img" -- . ':!bun.lock' ':!docs/internal' \
445
447
  | xargs -r sed -i "s|norskvideo/norsk-studio:$old_studio_img|norskvideo/norsk-studio:$new_studio_img|g"
446
448
  # And the tag on its own: a product can carry it unprefixed (onair2026's
447
449
  # committed template input has `"studioTag": "<tag>"`), which the
@@ -451,13 +453,13 @@ jobs:
451
453
  if [ "$old_studio_img" != "$old_studio_lib" ]; then
452
454
  # `|| true`: after the prefixed rewrite most repos have no bare
453
455
  # occurrence left, and git grep exits 1 on no match (pipefail).
454
- { git grep -lF "$old_studio_img" -- . ':!bun.lock' || true; } | xargs -r sed -i "s|$old_studio_img|$new_studio_img|g"
456
+ { git grep -lF "$old_studio_img" -- . ':!bun.lock' ':!docs/internal' || true; } | xargs -r sed -i "s|$old_studio_img|$new_studio_img|g"
455
457
  fi
456
458
  changed=1
457
459
  moved+=("- studio: $old_studio_img -> $new_studio_img")
458
460
  fi
459
461
  if [ -n "$new_studio_lib" ] && [ "$old_studio_lib" != "$new_studio_lib" ]; then
460
- git grep -lF "$old_studio_lib" -- . ':!bun.lock' | xargs -r sed -i "s|$old_studio_lib|$new_studio_lib|g"
462
+ git grep -lF "$old_studio_lib" -- . ':!bun.lock' ':!docs/internal' | xargs -r sed -i "s|$old_studio_lib|$new_studio_lib|g"
461
463
  changed=1
462
464
  moved+=("- studio-lib: $old_studio_lib -> $new_studio_lib")
463
465
  fi
@@ -26,6 +26,7 @@ export interface Canon {
26
26
  dprint: string;
27
27
  buildImageBootstrap: string;
28
28
  buildImage: string;
29
+ smoke: string;
29
30
  /** conventions/build-docs.yml — the embedded docs workflow a turnkey carries. */
30
31
  buildDocs: string;
31
32
  gitignoreCore: string;
@@ -53,6 +54,7 @@ export function loadCanon(): Canon {
53
54
  dprint: readFileSync(join(conventionsDir, "dprint.base.jsonc"), "utf8"),
54
55
  buildImageBootstrap: readFileSync(join(buildDir, "build-image.bootstrap.sh"), "utf8"),
55
56
  buildImage: readFileSync(join(conventionsDir, "build-image.yml"), "utf8"),
57
+ smoke: readFileSync(join(conventionsDir, "smoke.yml"), "utf8"),
56
58
  gitignoreCore: readFileSync(join(conventionsDir, "gitignore.core"), "utf8"),
57
59
  invariantsTemplate: readFileSync(join(import.meta.dir, "..", "testing", "INVARIANTS.template.md"), "utf8"),
58
60
  demoShim: readFileSync(join(conventionsDir, "demo.sh"), "utf8"),
@@ -223,6 +223,7 @@ function conventionFiles(ctx: ShapeContext, shape: ShapeModule): GeneratedFile[]
223
223
  { path: ".github/workflows/bump.yml", content: fillProduct(canon.bump) },
224
224
  { path: ".github/workflows/bot-prs.yml", content: canon.botPrs },
225
225
  { path: ".github/workflows/build-image.yml", content: fillProduct(canon.buildImage) },
226
+ { path: ".github/workflows/smoke.yml", content: fillProduct(canon.smoke) },
226
227
  { path: ".github/actions/ci-status-dispatch/action.yml", content: loadAsset("ci-status-dispatch.yml") },
227
228
  { path: "manifest.seed.json", content: seedJson(ctx) },
228
229
  { path: "licenseTemplate.json", content: licenseTemplateJson(ctx) },
@@ -257,9 +257,15 @@ refreshed \`~/.norsk-ctl/product-templates/<name>/compose.yml\` carries the pins
257
257
  so table and tests cannot drift apart.
258
258
  - **Layer 4 — studio-load round-trip** (\`tests/unit/loads-in-studio.test.ts\`):
259
259
  emitted YAML through Studio's real \`document.load()\`.
260
- - **Layer 5 — integration**: not scaffolded. Decide + record here whether this
261
- product carries one (unattended nightly bumps need it; see
260
+ - **Layer 5 — integration**: the smoke tier (\`tests/smoke.spec.ts\`, run by
261
+ the convention \`smoke.yml\` on PRs, main and the nightly bump). It launches
262
+ the built image, sends the camera1 sample to the SRT ingest and checks the
263
+ graph is up and the ingest connected. Grow its observations as the product
264
+ takes shape; add an \`integration.yml\` when a regression needs more than
265
+ one journey (see
262
266
  \`node_modules/@norskvideo/ctl-dev-kit/conventions/integration-testing.md\`).
267
+ Locally: \`bun run build:image && bun run test:smoke\` with
268
+ \`NORSK_LICENSE_FILE\` set.
263
269
 
264
270
  Byte-snapshots (\`tests/unit/workflow-snapshot.test.ts\`) pin the emitted wire
265
271
  bytes; \`UPDATE_SNAPSHOTS=1\` refreshes deliberately.
@@ -303,6 +309,7 @@ function rootPackageJson(ctx: ShapeContext): string {
303
309
  test: "bun run test:unit",
304
310
  "test:unit": `bun test shared/src/ backend/src/ ${hasComponents ? "components/src/ " : ""}tests/unit/`,
305
311
  "test:image": "bun test tests/image/",
312
+ "test:smoke": "NORSK_CTL_ENV=test bun test tests/smoke.spec.ts",
306
313
  "check:drift": "bun run node_modules/@norskvideo/ctl-dev-kit/conventions/check-drift.ts",
307
314
  "docs:check":
308
315
  "bun run node_modules/@norskvideo/ctl-dev-kit/docs/path-existence.ts && bun run node_modules/@norskvideo/ctl-dev-kit/docs/light-pages.ts && bun run node_modules/@norskvideo/ctl-dev-kit/docs/journeys.ts",
@@ -1825,6 +1832,49 @@ exec bun run demo -- up --mode image --daemon reuse
1825
1832
  `;
1826
1833
  }
1827
1834
 
1835
+ function smokeSpecTs(ctx: ShapeContext): string {
1836
+ return `// The smoke tier: one journey through the built image, run by
1837
+ // @norskvideo/ctl-test-harness/smoke on PRs, main and the nightly bump.
1838
+ // smoke.yml builds the image and sets SMOKE_IMAGE_TAG; locally,
1839
+ // \`bun run build:image && bun run test:smoke\` with NORSK_LICENSE_FILE set.
1840
+ // The tag must be one the licence names (licenseTemplate.json).
1841
+ //
1842
+ // The camera1 sample goes to the SRT ingest. What it proves: the graph is up
1843
+ // and the ingest takes the feed. Grow the observations as the product takes
1844
+ // shape: they are what an unattended nightly bump is trusted on.
1845
+ import { test } from "bun:test";
1846
+ import { hashSlot } from "@norskvideo/ctl-test-harness/harness-config";
1847
+ import { runProductSmoke } from "@norskvideo/ctl-test-harness/smoke";
1848
+
1849
+ // The listener inside the media container (the configured default), which the
1850
+ // pump dials on the docker network.
1851
+ const LISTENER_PORT = 5001;
1852
+ // The host side of that publish, moved off 5001: other products' smokes (and a
1853
+ // running demo) hold it on a shared runner.
1854
+ const INGEST_HOST_PORT = 27001 + hashSlot("${ctx.name}", 40) * 4;
1855
+
1856
+ test(
1857
+ "${ctx.name}: the graph comes up and takes the SRT feed",
1858
+ () =>
1859
+ runProductSmoke("${ctx.name}", {
1860
+ product: {
1861
+ name: "${ctx.name}",
1862
+ register: { image: process.env.SMOKE_IMAGE_TAG ?? "${ctx.name}:dev" },
1863
+ },
1864
+ template: { name: "default" },
1865
+ params: [\`INGEST_PORT=\${INGEST_HOST_PORT}\`],
1866
+ hardware: "none",
1867
+ sources: [{ port: LISTENER_PORT, name: "input", streamId: "input", preset: "camera1" }],
1868
+ observe: [
1869
+ { kind: "components", ids: ["ingest", "egest"] },
1870
+ { kind: "srt-connected", componentId: "ingest" },
1871
+ ],
1872
+ }),
1873
+ 600_000,
1874
+ );
1875
+ `;
1876
+ }
1877
+
1828
1878
  function demoSpecTs(ctx: ShapeContext): string {
1829
1879
  return `// The ${ctx.name} demo (fleet review 05-demo): the manifest's default
1830
1880
  // template with the daemon's camera1 sample pumped into the SRT ingest. Run by
@@ -2222,6 +2272,7 @@ export const turnkey: ShapeModule = {
2222
2272
  { path: "scripts/with-display.sh", content: ctx.canon.withDisplay, executable: true },
2223
2273
  { path: "scripts/docs-handover.sh", content: DOCS_HANDOVER_SH, executable: true },
2224
2274
  { path: "tests/demo.spec.ts", content: demoSpecTs(ctx) },
2275
+ { path: "tests/smoke.spec.ts", content: smokeSpecTs(ctx) },
2225
2276
  { path: "tests/unit/demo-spec.test.ts", content: DEMO_SPEC_TEST_TS },
2226
2277
  { path: "shared/package.json", content: sharedPackageJson(ctx) },
2227
2278
  { path: "shared/tsconfig.json", content: WORKSPACE_TSCONFIG },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@norskvideo/ctl-dev-kit",
3
- "version": "0.2.73",
3
+ "version": "0.2.75",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./create-product": "./create-product/create-product.ts",