@norskvideo/ctl-dev-kit 0.2.68 → 0.2.69

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.
@@ -412,6 +412,38 @@ export function productLogsProblems(repoRoot: string): string[] {
412
412
  ];
413
413
  }
414
414
 
415
+ /** The Bun the dev-kit's build/ctl-shell.nix pins: the version its pin's URL
416
+ * line names (`… .tar.gz"; # bun 1.4.2`). The shell runs it; the product's
417
+ * image runs package.json's "packageManager", which must name the same. */
418
+ export function pinnedBunVersion(ctlShell: string): string | undefined {
419
+ return /archive\/[0-9a-f]{40}\.tar\.gz"; # bun (\d+\.\d+\.\d+)/.exec(ctlShell)?.[1];
420
+ }
421
+
422
+ /** package.json's "packageManager" against the shell's Bun. They drifted once
423
+ * already: the fleet's shells and images ran 1.3.13 by agreement alone, and a
424
+ * move to 1.4.2 (1.3.13's HTTP/2 client segfaulted) had to touch every repo. */
425
+ export function bunVersionProblems(repoRoot: string, ctlShellCanonical: string): string[] {
426
+ const want = pinnedBunVersion(ctlShellCanonical);
427
+ if (want === undefined) {
428
+ return ["@norskvideo/ctl-dev-kit build/ctl-shell.nix names no Bun version on its pin (`# bun X.Y.Z`)."];
429
+ }
430
+ const pkgPath = join(repoRoot, "package.json");
431
+ if (!existsSync(pkgPath)) return [];
432
+ let have: unknown;
433
+ try {
434
+ have = (JSON.parse(readFileSync(pkgPath, "utf8")) as { packageManager?: unknown }).packageManager;
435
+ } catch (e) {
436
+ return [`package.json is not parseable JSON (${e instanceof Error ? e.message : String(e)}).`];
437
+ }
438
+ return have === `bun@${want}`
439
+ ? []
440
+ : [
441
+ `package.json "packageManager" is ${JSON.stringify(have ?? null)}, not "bun@${want}": it is the Bun the ` +
442
+ "product's image runs, and must be the one the shell runs (@norskvideo/ctl-dev-kit build/ctl-shell.nix). " +
443
+ "Re-sync to set it.",
444
+ ];
445
+ }
446
+
415
447
  // A file that must be a verbatim, byte-for-byte copy of its dev-kit canonical.
416
448
  function verbatimProblem(
417
449
  repoRoot: string,
@@ -862,6 +894,7 @@ export function checkDrift(repoRoot: string, canonical: CanonicalBytes, opts: Ch
862
894
 
863
895
  for (const p of demoProblems(repoRoot, canonical.demoShim)) push(p);
864
896
  for (const p of productLogsProblems(repoRoot)) push(p);
897
+ for (const p of bunVersionProblems(repoRoot, canonical.ctlShell)) push(p);
865
898
  for (const p of ctlPinProblems(repoRoot)) push(p);
866
899
 
867
900
  return { ok: problems.length === 0, problems };
@@ -48,6 +48,7 @@ import {
48
48
  PRODUCT_SENTINEL,
49
49
  type ProductShape,
50
50
  PUBLISH_IMAGE_LINE,
51
+ pinnedBunVersion,
51
52
  RETIRED_BUMP_WORKFLOWS,
52
53
  RUNNER_LINE,
53
54
  resolveRepoName,
@@ -401,6 +402,29 @@ function syncDocsTheme(repoRoot: string, r: SyncReport): void {
401
402
  for (const rel of adrift) r.written.push(`${DOCS_SITE_DIR}/${rel}`);
402
403
  }
403
404
 
405
+ // package.json "packageManager": the Bun the product's image runs, set to the
406
+ // one the shell pins (build/ctl-shell.nix), so a Bun move reaches every
407
+ // product through its sync. Only that field's value is rewritten, so the
408
+ // file's own formatting stays. A package.json with no field is left for the
409
+ // gate to flag.
410
+ function syncPackageManager(repoRoot: string, ctlShell: string, r: SyncReport): void {
411
+ const want = pinnedBunVersion(ctlShell);
412
+ const path = join(repoRoot, "package.json");
413
+ if (want === undefined || !existsSync(path)) return;
414
+ const current = readFileSync(path, "utf8");
415
+ const field = /("packageManager"\s*:\s*")[^"]*(")/;
416
+ if (!field.test(current)) {
417
+ r.skipped.push("package.json (no packageManager to set)");
418
+ return;
419
+ }
420
+ writeIfChanged(
421
+ path,
422
+ current.replace(field, (_m, a: string, b: string) => `${a}bun@${want}${b}`),
423
+ "package.json",
424
+ r.written,
425
+ );
426
+ }
427
+
404
428
  export function syncDrift(
405
429
  repoRoot: string,
406
430
  canonical: CanonicalBytes,
@@ -448,6 +472,7 @@ export function syncDrift(
448
472
 
449
473
  mkdirSync(dirname(join(repoRoot, CTL_SHELL_PATH)), { recursive: true });
450
474
  writeIfChanged(join(repoRoot, CTL_SHELL_PATH), canonical.ctlShell, CTL_SHELL_PATH, r.written);
475
+ syncPackageManager(repoRoot, canonical.ctlShell, r);
451
476
  syncFlake(repoRoot, flakeCanon, r);
452
477
  syncClaude(repoRoot, canonical.core, r);
453
478
  syncGitignore(repoRoot, canonical.gitignoreCore, r);
@@ -13,6 +13,8 @@
13
13
  // from the schema package, addComponent/stubbedLibrary with a THROWING
14
14
  // validate(), INVARIANTS + parity + byte-snapshot + studio-load wired into
15
15
  // test:unit, and an image smoke tier.
16
+
17
+ import { pinnedBunVersion } from "../conventions/check-drift.ts";
16
18
  import {
17
19
  type GeneratedFile,
18
20
  PRODUCT_ZOD,
@@ -362,7 +364,8 @@ function rootPackageJson(ctx: ShapeContext): string {
362
364
  "@types/node": "^22.10.0",
363
365
  typescript: "^5.7.2",
364
366
  },
365
- packageManager: "bun@1.3.13",
367
+ // The shell's Bun (build/ctl-shell.nix): the drift gate holds the two together.
368
+ packageManager: `bun@${pinnedBunVersion(ctx.canon.ctlShell)}`,
366
369
  overrides: {
367
370
  ws: "^8.20.1",
368
371
  "shell-quote": "^1.9.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@norskvideo/ctl-dev-kit",
3
- "version": "0.2.68",
3
+ "version": "0.2.69",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./create-product": "./create-product/create-product.ts",