@rightkit/release 0.2.67 → 0.2.68

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-release.mjs CHANGED
@@ -322,6 +322,7 @@ try {
322
322
  env,
323
323
  [managedCargoTarget ?? env.CARGO_TARGET_DIR, path.join(appRoot, ".cache")],
324
324
  path.join(stateRoot, "stall-build.log"),
325
+ { livenessOwnerTimeoutMs: target.package.livenessOwnerTimeoutMs ?? null },
325
326
  );
326
327
  checkpoint(stateRoot, "build_complete");
327
328
  checkpoint(stateRoot, "signed");
@@ -464,8 +465,8 @@ function collectToolVersions(packageManager) {
464
465
  }
465
466
  }
466
467
 
467
- async function runProgress(cmd, runArgs, cwd, env, watchDir, diagnosticPath = null) {
468
- const { inactivityMs, absoluteMs } = releaseProgressLimits(process.env);
468
+ async function runProgress(cmd, runArgs, cwd, env, watchDir, diagnosticPath = null, { livenessOwnerTimeoutMs = null } = {}) {
469
+ const { inactivityMs, absoluteMs } = releaseProgressLimits(env, { livenessOwnerTimeoutMs });
469
470
  let lastProgress = Date.now();
470
471
  const watchDirs = Array.isArray(watchDir) ? watchDir : [watchDir];
471
472
  let lastMtime = Math.max(...watchDirs.map(newestMtime));
@@ -476,7 +477,8 @@ async function runProgress(cmd, runArgs, cwd, env, watchDir, diagnosticPath = nu
476
477
  let managedRequestId = null;
477
478
  const settleResolve = () => { if (!settled) { settled = true; resolve(); } };
478
479
  const settleReject = (error) => { if (!settled) { settled = true; reject(error); } };
479
- child = spawn(cmd, runArgs, { cwd, env, stdio: ["inherit", "pipe", "pipe"], windowsHide: true, shell: process.platform === "win32", detached: process.platform !== "win32" });
480
+ const useShell = process.platform === "win32" && !/\.(?:exe|com)$/i.test(cmd);
481
+ child = spawn(cmd, runArgs, { cwd, env, stdio: ["inherit", "pipe", "pipe"], windowsHide: true, shell: useShell, detached: process.platform !== "win32" });
480
482
  try {
481
483
  if (child.pid && heavySlot) heavySlot.trackChild(child.pid, cmd);
482
484
  } catch (error) {
@@ -54,6 +54,12 @@ test("production builds package from the real primary app checkout", () => {
54
54
  assert.match(source, /sealRelease\(\{ configRoot: appRoot,/);
55
55
  });
56
56
 
57
+ test("Windows native executables preserve arguments containing spaces", () => {
58
+ assert.match(source, /const useShell = process\.platform === "win32" && !\/\\\.\(\?:exe\|com\)\$\/i\.test\(cmd\);/);
59
+ assert.match(source, /shell: useShell/);
60
+ assert.doesNotMatch(source, /shell: process\.platform === "win32"/);
61
+ });
62
+
57
63
  test("sealing resolves installer and updater artifacts against the broker target on a managed host", () => {
58
64
  assert.match(source, /sealRelease\(\{ configRoot: appRoot, managedCargoTarget,/);
59
65
  assert.match(source, /function sealRelease\(\{ configRoot, managedCargoTarget,/);
@@ -157,6 +163,11 @@ test("CPU sampling degrades to the old behaviour rather than inventing a verdict
157
163
  assert.equal(processGroupCpuSeconds(123, { platform: "darwin", run: ok("") }), null);
158
164
  });
159
165
 
166
+ test("package liveness owner timeout floors outer watchdog through shared release limits", () => {
167
+ assert.match(source, /livenessOwnerTimeoutMs: target\.package\.livenessOwnerTimeoutMs \?\? null/);
168
+ assert.match(source, /releaseProgressLimits\(env, \{ livenessOwnerTimeoutMs \}\)/);
169
+ });
170
+
160
171
  // Regression: the managed-host gate must not depend on RIGHTKIT_BUILD_BROKER_SOCKET
161
172
  // alone. That variable is exported by login shells only, while the managed cargo
162
173
  // shim is on PATH in every shell — so an agent shell took the Cache V2 branch,
package/cargo-target.mjs CHANGED
@@ -13,11 +13,12 @@ import { execFileSync } from "node:child_process";
13
13
  import path from "node:path";
14
14
  import { fileURLToPath } from "node:url";
15
15
 
16
- export function resolveTargetRoot(manifestPath) {
16
+ export function resolveTargetRoot(manifestPath, options = {}) {
17
17
  if (!manifestPath) throw new Error("resolveTargetRoot requires a Cargo.toml manifest path");
18
+ const execute = options.execFileSync ?? execFileSync;
18
19
  let output;
19
20
  try {
20
- output = execFileSync(
21
+ output = execute(
21
22
  "cargo",
22
23
  ["metadata", "--offline", "--format-version", "1", "--no-deps", "--manifest-path", manifestPath],
23
24
  { cwd: path.dirname(manifestPath), encoding: "utf8", maxBuffer: 64 * 1024 * 1024 },
@@ -1,5 +1,5 @@
1
1
  import assert from "node:assert/strict";
2
- import { chmodSync, mkdtempSync, mkdirSync, writeFileSync } from "node:fs";
2
+ import { mkdtempSync, mkdirSync, writeFileSync } from "node:fs";
3
3
  import os from "node:os";
4
4
  import path from "node:path";
5
5
  import { spawnSync } from "node:child_process";
@@ -11,91 +11,42 @@ import { resolveTargetRoot } from "./cargo-target.mjs";
11
11
  const here = path.dirname(fileURLToPath(import.meta.url));
12
12
  const cli = path.join(here, "cargo-target.mjs");
13
13
 
14
- // A fake `cargo` on PATH, ahead of the real one, so metadata success/failure
15
- // shapes are deterministic and don't depend on the host's actual crate graph.
16
- // It branches on a marker embedded in the manifest path so each test controls
17
- // its own outcome independently.
18
- function fakeCargoBin() {
14
+ function fakeCargo() {
19
15
  const bin = mkdtempSync(path.join(os.tmpdir(), "rightkit-fake-cargo-"));
20
- const script = path.join(bin, process.platform === "win32" ? "cargo.cmd" : "cargo");
21
- const body = process.platform === "win32"
22
- ? [
23
- "@echo off",
24
- "setlocal enabledelayedexpansion",
25
- "set ARGS=%*",
26
- "echo %ARGS% | findstr /C:\"fail-manifest\" >nul && (echo boom 1>&2 & exit /b 1)",
27
- "echo %ARGS% | findstr /C:\"bad-target-manifest\" >nul && (echo {\"target_directory\":\"relative/target\"} & exit /b 0)",
28
- "echo {\"target_directory\":\"" + path.join(bin, "target").replaceAll("\\", "\\\\") + "\"}",
29
- ].join("\r\n")
30
- : [
31
- "#!/bin/sh",
32
- 'case "$*" in',
33
- ' *fail-manifest*) echo boom 1>&2; exit 1 ;;',
34
- ' *bad-target-manifest*) echo \'{"target_directory":"relative/target"}\'; exit 0 ;;',
35
- ` *) echo '{"target_directory":"${path.join(bin, "target")}"}'; exit 0 ;;`,
36
- "esac",
37
- ].join("\n");
38
- writeFileSync(script, body);
39
- if (process.platform !== "win32") chmodSync(script, 0o755);
40
- return bin;
41
- }
42
-
43
- function withFakeCargo(fn) {
44
- const bin = fakeCargoBin();
45
- const env = { ...process.env, PATH: `${bin}${path.delimiter}${process.env.PATH ?? ""}` };
46
- return fn({ bin, env });
16
+ return {
17
+ bin,
18
+ execFileSync(_command, args) {
19
+ const manifest = args.at(-1);
20
+ if (manifest.includes("fail-manifest")) throw new Error("boom");
21
+ if (manifest.includes("bad-target-manifest")) return '{"target_directory":"relative/target"}';
22
+ return JSON.stringify({ target_directory: path.join(bin, "target") });
23
+ },
24
+ };
47
25
  }
48
26
 
49
27
  test("resolveTargetRoot returns the absolute target_directory Cargo metadata reports", () => {
50
- withFakeCargo(({ bin, env }) => {
51
- const manifestDir = mkdtempSync(path.join(os.tmpdir(), "rightkit-cargo-target-fixture-"));
52
- const manifest = path.join(manifestDir, "Cargo.toml");
53
- writeFileSync(manifest, "[package]\nname = \"fixture\"\nversion = \"0.0.0\"\n");
54
- const previousPath = process.env.PATH;
55
- process.env.PATH = env.PATH;
56
- try {
57
- const result = resolveTargetRoot(manifest);
58
- assert.equal(result, path.resolve(path.join(bin, "target")));
59
- } finally {
60
- process.env.PATH = previousPath;
61
- }
62
- });
28
+ const { bin, execFileSync } = fakeCargo();
29
+ const manifest = path.join(mkdtempSync(path.join(os.tmpdir(), "rightkit-cargo-target-fixture-")), "Cargo.toml");
30
+ const result = resolveTargetRoot(manifest, { execFileSync });
31
+ assert.equal(result, path.resolve(path.join(bin, "target")));
63
32
  });
64
33
 
65
34
  test("resolveTargetRoot fails closed, naming the manifest, when target_directory is not absolute", () => {
66
- withFakeCargo(({ env }) => {
67
- const manifestDir = mkdtempSync(path.join(os.tmpdir(), "rightkit-cargo-target-fixture-"));
68
- const manifest = path.join(manifestDir, "bad-target-manifest-Cargo.toml");
69
- writeFileSync(manifest, "[package]\nname = \"fixture\"\nversion = \"0.0.0\"\n");
70
- const previousPath = process.env.PATH;
71
- process.env.PATH = env.PATH;
72
- try {
73
- assert.throws(
74
- () => resolveTargetRoot(manifest),
75
- new RegExp(`metadata for .*bad-target-manifest-Cargo\\.toml did not report an absolute target_directory`),
76
- );
77
- } finally {
78
- process.env.PATH = previousPath;
79
- }
80
- });
35
+ const { execFileSync } = fakeCargo();
36
+ const manifest = path.join(mkdtempSync(path.join(os.tmpdir(), "rightkit-cargo-target-fixture-")), "bad-target-manifest-Cargo.toml");
37
+ assert.throws(
38
+ () => resolveTargetRoot(manifest, { execFileSync }),
39
+ new RegExp(`metadata for .*bad-target-manifest-Cargo\\.toml did not report an absolute target_directory`),
40
+ );
81
41
  });
82
42
 
83
43
  test("resolveTargetRoot fails closed, naming the manifest, when the metadata command exits non-zero", () => {
84
- withFakeCargo(({ env }) => {
85
- const manifestDir = mkdtempSync(path.join(os.tmpdir(), "rightkit-cargo-target-fixture-"));
86
- const manifest = path.join(manifestDir, "fail-manifest-Cargo.toml");
87
- writeFileSync(manifest, "[package]\nname = \"fixture\"\nversion = \"0.0.0\"\n");
88
- const previousPath = process.env.PATH;
89
- process.env.PATH = env.PATH;
90
- try {
91
- assert.throws(
92
- () => resolveTargetRoot(manifest),
93
- new RegExp(`metadata failed for .*fail-manifest-Cargo\\.toml`),
94
- );
95
- } finally {
96
- process.env.PATH = previousPath;
97
- }
98
- });
44
+ const { execFileSync } = fakeCargo();
45
+ const manifest = path.join(mkdtempSync(path.join(os.tmpdir(), "rightkit-cargo-target-fixture-")), "fail-manifest-Cargo.toml");
46
+ assert.throws(
47
+ () => resolveTargetRoot(manifest, { execFileSync }),
48
+ new RegExp(`metadata failed for .*fail-manifest-Cargo\\.toml`),
49
+ );
99
50
  });
100
51
 
101
52
  test("resolveTargetRoot throws when no manifest path is given", () => {
@@ -104,26 +55,24 @@ test("resolveTargetRoot throws when no manifest path is given", () => {
104
55
  });
105
56
 
106
57
  test("CLI mode writes only the resolved path to stdout on success", () => {
107
- withFakeCargo(({ bin, env }) => {
108
- const manifestDir = mkdtempSync(path.join(os.tmpdir(), "rightkit-cargo-target-fixture-"));
109
- const manifest = path.join(manifestDir, "Cargo.toml");
110
- writeFileSync(manifest, "[package]\nname = \"fixture\"\nversion = \"0.0.0\"\n");
111
- const result = spawnSync(process.execPath, [cli, manifest], { encoding: "utf8", env });
112
- assert.equal(result.status, 0, result.stderr);
113
- assert.equal(result.stdout.trim(), path.resolve(path.join(bin, "target")));
114
- });
58
+ const manifestDir = mkdtempSync(path.join(os.tmpdir(), "rightkit-cargo-target-fixture-"));
59
+ const manifest = path.join(manifestDir, "Cargo.toml");
60
+ mkdirSync(path.join(manifestDir, "src"));
61
+ writeFileSync(path.join(manifestDir, "src", "lib.rs"), "");
62
+ writeFileSync(manifest, "[package]\nname = \"fixture\"\nversion = \"0.0.0\"\n");
63
+ const result = spawnSync(process.execPath, [cli, manifest], { encoding: "utf8" });
64
+ assert.equal(result.status, 0, result.stderr);
65
+ assert.equal(path.isAbsolute(result.stdout.trim()), true);
115
66
  });
116
67
 
117
68
  test("CLI mode writes a message to stderr and exits 1 on failure", () => {
118
- withFakeCargo(({ env }) => {
119
- const manifestDir = mkdtempSync(path.join(os.tmpdir(), "rightkit-cargo-target-fixture-"));
120
- const manifest = path.join(manifestDir, "fail-manifest-Cargo.toml");
121
- writeFileSync(manifest, "[package]\nname = \"fixture\"\nversion = \"0.0.0\"\n");
122
- const result = spawnSync(process.execPath, [cli, manifest], { encoding: "utf8", env });
123
- assert.equal(result.status, 1);
124
- assert.equal(result.stdout, "");
125
- assert.match(result.stderr, /metadata failed for .*fail-manifest-Cargo\.toml/);
126
- });
69
+ const manifestDir = mkdtempSync(path.join(os.tmpdir(), "rightkit-cargo-target-fixture-"));
70
+ const manifest = path.join(manifestDir, "fail-manifest-Cargo.toml");
71
+ writeFileSync(manifest, "not valid toml");
72
+ const result = spawnSync(process.execPath, [cli, manifest], { encoding: "utf8" });
73
+ assert.equal(result.status, 1);
74
+ assert.equal(result.stdout, "");
75
+ assert.match(result.stderr, /metadata failed for .*fail-manifest-Cargo\.toml/);
127
76
  });
128
77
 
129
78
  test("CLI mode requires a manifest argument", () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rightkit/release",
3
- "version": "0.2.67",
3
+ "version": "0.2.68",
4
4
  "description": "Portable Right Suite release CLI/SDK: native-host signed installers, updater artifacts, hardening, immutable GitHub Release upload, and add-on adoption.",
5
5
  "license": "MIT OR Apache-2.0",
6
6
  "type": "module",
@@ -25,7 +25,7 @@
25
25
  "directory": "tools/rightkit/packages/release"
26
26
  },
27
27
  "scripts": {
28
- "test": "node --test *.test.mjs",
28
+ "test": "node --test --test-concurrency=1 --test-force-exit *.test.mjs",
29
29
  "test:registry-parity": "node registry-parity.mjs --allow-unpublished",
30
30
  "doctor:all": "node --test right-suite-contract.test.mjs",
31
31
  "verify:standalone": "node standalone-clone-verify.mjs"
@@ -1,6 +1,7 @@
1
1
  import { spawn } from "node:child_process";
2
2
 
3
3
  const MANAGED_REQUEST_PATTERN = /\brightkit(?: managed-agent)?: request ([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\b/gi;
4
+ export const RELEASE_LIVENESS_OWNER_MARGIN_MS = 5 * 60 * 1000;
4
5
 
5
6
  export function latestManagedRequestId(value, fallback = null) {
6
7
  let latest = fallback;
@@ -13,11 +14,17 @@ export function firstManagedRequestId(value) {
13
14
  return MANAGED_REQUEST_PATTERN.exec(String(value ?? ""))?.[1]?.toLowerCase() ?? null;
14
15
  }
15
16
 
16
- export function releaseProgressLimits(env = process.env) {
17
- const inactivityMs = Number(env.RIGHT_RELEASE_STALL_MS ?? 10 * 60 * 1000);
17
+ export function releaseProgressLimits(env = process.env, { livenessOwnerTimeoutMs = null } = {}) {
18
+ const configuredInactivityMs = Number(env.RIGHT_RELEASE_STALL_MS ?? 10 * 60 * 1000);
18
19
  const absoluteMs = Number(env.RIGHT_RELEASE_ABSOLUTE_MS ?? 4 * 60 * 60 * 1000);
19
- if (![inactivityMs, absoluteMs].every((value) => Number.isFinite(value) && value > 0)) throw new Error("release progress limits must be positive finite milliseconds");
20
- if (absoluteMs <= inactivityMs) throw new Error("RIGHT_RELEASE_ABSOLUTE_MS must exceed RIGHT_RELEASE_STALL_MS");
20
+ const ownerTimeoutMs = livenessOwnerTimeoutMs === null ? null : Number(livenessOwnerTimeoutMs);
21
+ if (![configuredInactivityMs, absoluteMs, ...(ownerTimeoutMs === null ? [] : [ownerTimeoutMs])].every((value) => Number.isFinite(value) && value > 0)) {
22
+ throw new Error("release progress limits must be positive finite milliseconds");
23
+ }
24
+ const inactivityMs = ownerTimeoutMs === null
25
+ ? configuredInactivityMs
26
+ : Math.max(configuredInactivityMs, ownerTimeoutMs + RELEASE_LIVENESS_OWNER_MARGIN_MS);
27
+ if (absoluteMs <= inactivityMs) throw new Error("RIGHT_RELEASE_ABSOLUTE_MS must exceed effective release inactivity limit");
21
28
  return Object.freeze({ inactivityMs, absoluteMs });
22
29
  }
23
30
 
@@ -10,6 +10,23 @@ test("release progress stays bounded without killing a healthy long native build
10
10
  assert.throws(() => releaseProgressLimits({ RIGHT_RELEASE_STALL_MS: "nope" }), /positive finite/);
11
11
  });
12
12
 
13
+ test("declared inner liveness owner always exits before the outer watchdog", () => {
14
+ const ownerTimeoutMs = 45 * 60 * 1000;
15
+ assert.deepEqual(
16
+ releaseProgressLimits({}, { livenessOwnerTimeoutMs: ownerTimeoutMs }),
17
+ { inactivityMs: 50 * 60 * 1000, absoluteMs: 4 * 60 * 60 * 1000 },
18
+ );
19
+ assert.deepEqual(
20
+ releaseProgressLimits({ RIGHT_RELEASE_STALL_MS: String(60 * 60 * 1000) }, { livenessOwnerTimeoutMs: ownerTimeoutMs }),
21
+ { inactivityMs: 60 * 60 * 1000, absoluteMs: 4 * 60 * 60 * 1000 },
22
+ );
23
+ assert.throws(
24
+ () => releaseProgressLimits({ RIGHT_RELEASE_ABSOLUTE_MS: String(49 * 60 * 1000) }, { livenessOwnerTimeoutMs: ownerTimeoutMs }),
25
+ /must exceed effective release inactivity limit/,
26
+ );
27
+ assert.throws(() => releaseProgressLimits({}, { livenessOwnerTimeoutMs: 0 }), /positive finite/);
28
+ });
29
+
13
30
  test("latest managed request id follows streamed root Cargo requests", () => {
14
31
  const first = "11111111-1111-4111-8111-111111111111";
15
32
  const second = "22222222-2222-4222-8222-222222222222";
@@ -21,7 +21,7 @@ test('registry parity distinguishes an unpublished version from registry failure
21
21
  assert.deepEqual(await verifyRegistryParity({ allowUnpublished: true, fetchImpl: notFound }), {
22
22
  status: 'UNPUBLISHED',
23
23
  name: '@rightkit/release',
24
- version: '0.2.67',
24
+ version: '0.2.68',
25
25
  });
26
26
  await assert.rejects(
27
27
  verifyRegistryParity({ fetchImpl: notFound }),
package/release.mjs CHANGED
@@ -521,11 +521,12 @@ function run(cmd, runArgs, cwd, env = {}, options = {}) {
521
521
  }
522
522
  const started = Date.now();
523
523
  return new Promise((resolve) => {
524
+ const useShell = process.platform === "win32" && !/\.(?:exe|com)$/i.test(cmd);
524
525
  const child = spawn(cmd, runArgs, {
525
526
  cwd,
526
527
  env: { ...process.env, ...releaseEnv, ...env },
527
528
  stdio: "inherit",
528
- shell: process.platform === "win32",
529
+ shell: useShell,
529
530
  windowsHide: true,
530
531
  });
531
532
  const timer = options.timeoutMs
package/release.test.mjs CHANGED
@@ -199,6 +199,13 @@ test("release worker reuses owned process-tree termination for Windows and remot
199
199
  assert.match(source, /function killProcessTree\(pid\) \{\s*terminateProcessTree\(pid\);\s*\}/s);
200
200
  });
201
201
 
202
+ test("Windows native release tools preserve artifact arguments containing spaces", () => {
203
+ const source = readFileSync(release, "utf8");
204
+ assert.match(source, /const useShell = process\.platform === "win32" && !\/\\\.\(\?:exe\|com\)\$\/i\.test\(cmd\);/);
205
+ assert.match(source, /shell: useShell/);
206
+ assert.doesNotMatch(source, /shell: process\.platform === "win32"/);
207
+ });
208
+
202
209
  test("accepts patch and exposes it to the signed package command", () => {
203
210
  const result = run(fixture(), "--tier=patch");
204
211
  assert.equal(result.status, 0, result.stderr);
@@ -481,18 +481,25 @@ test("RightKit exposes one current version manifest", () => {
481
481
  assert.equal(versions.npm["@rightkit/updates"], "0.2.3");
482
482
  assert.deepEqual(versions.stagedNpm, {
483
483
  "@rightkit/ax": "0.2.0",
484
- "@rightkit/git": "0.2.0",
485
- "@rightkit/hooks": "0.1.0",
486
- "@rightkit/legal": "0.3.0",
484
+ "@rightkit/git": "0.2.1",
485
+ "@rightkit/hooks": "0.1.1",
486
+ "@rightkit/legal": "0.3.1",
487
487
  "@rightkit/legal-ui": "0.1.1",
488
- "@rightkit/license": "0.1.6",
489
- "@rightkit/release": "0.2.67",
490
- "@rightkit/qa": "0.2.0",
488
+ "@rightkit/license": "0.1.7",
489
+ "@rightkit/logs": "0.1.4",
490
+ "@rightkit/platform-ui": "0.1.1",
491
+ "@rightkit/qa": "0.2.1",
492
+ "@rightkit/release": "0.2.68",
493
+ "@rightkit/tauri": "0.1.1",
494
+ "@rightkit/updates": "0.2.4",
491
495
  });
492
496
  assert.deepEqual(versions.legacyNpm, {
493
497
  "@rightkit/legal-ui": ["0.1.0"],
494
- "@rightkit/release": ["0.2.22", "0.2.29", "0.2.30", "0.2.31", "0.2.41", "0.2.42", "0.2.43", "0.2.44", "0.2.45", "0.2.46", "0.2.49", "0.2.50", "0.2.51", "0.2.53", "0.2.54", "0.2.55", "0.2.56", "0.2.61", "0.2.62", "0.2.63", "0.2.64", "0.2.65", "0.2.66"],
495
- "@rightkit/qa": ["0.1.0"],
498
+ "@rightkit/hooks": ["0.1.0"],
499
+ "@rightkit/legal": ["0.3.0"],
500
+ "@rightkit/license": ["0.1.6"],
501
+ "@rightkit/release": ["0.2.22", "0.2.29", "0.2.30", "0.2.31", "0.2.41", "0.2.42", "0.2.43", "0.2.44", "0.2.45", "0.2.46", "0.2.49", "0.2.50", "0.2.51", "0.2.53", "0.2.54", "0.2.55", "0.2.56", "0.2.61", "0.2.62", "0.2.63", "0.2.64", "0.2.65", "0.2.66", "0.2.67"],
502
+ "@rightkit/qa": ["0.1.0", "0.2.0"],
496
503
  });
497
504
  assert.ok(
498
505
  new Set([versions.npm["@rightkit/release"], ...versions.legacyNpm["@rightkit/release"]]).has("0.2.42"),
@@ -853,32 +860,26 @@ test("consuming suite apps resolve Cargo build output from cargo metadata, never
853
860
  const cargoTargetRootHelpers = [
854
861
  "coderight/apps/coderight-tauri/scripts/lib/target-root.mjs",
855
862
  "heardright/tauri-app-next/scripts/lib/target-root.mjs",
863
+ "genright/scripts/lib/target-root.mjs",
856
864
  "mailright/scripts/lib/target-root.mjs",
857
865
  "orthic/scripts/lib/target-root.mjs",
858
866
  "viewright/scripts/lib/target-root.mjs",
859
867
  "membrane/apps/membrane-hub/scripts/lib/target-root.mjs",
860
868
  ];
861
869
 
862
- test("shared target-root helpers still resolve build output via cargo metadata's target_directory", () => {
870
+ test("consumer target-root helpers delegate to RightKit's canonical resolver", () => {
863
871
  for (const helperPath of cargoTargetRootHelpers) {
864
872
  const appRoot = path.join(workspace, helperPath.split("/scripts/lib/target-root.mjs")[0]);
865
873
  if (!existsSync(path.join(appRoot, "package.json"))) continue;
866
874
  const fullPath = path.join(workspace, helperPath);
867
875
  assert.ok(existsSync(fullPath), `${helperPath} is missing; the app must keep its shared cargo-metadata target resolver`);
868
876
  const source = readFileSync(fullPath, "utf8");
869
- assert.match(source, /cargo/, `${helperPath} must invoke cargo`);
870
- assert.match(source, /metadata/, `${helperPath} must call cargo metadata`);
871
- assert.match(source, /target_directory/, `${helperPath} must read target_directory from cargo metadata's output`);
877
+ assert.match(source, /@rightkit\/release\/cargo-target\.mjs/, `${helperPath} must import RightKit's resolver`);
878
+ assert.match(source, /resolveTargetRoot/, `${helperPath} must delegate target resolution to RightKit`);
872
879
  }
873
880
  });
874
881
 
875
- // After the app migration lands, every consuming app's target-root helper must
876
- // become a thin re-export of the shared tools/rightkit resolveTargetRoot
877
- // (cargo-target.mjs) rather than its own copy of the cargo-metadata call. Until
878
- // that migration lands, cargoTargetRootHelpers above still point at full local
879
- // implementations, so this stays skipped to avoid failing the still-unmigrated
880
- // app repos. Enable it once every app's target-root.mjs is a re-export shim.
881
- test("consuming app target-root helpers are thin re-export shims, not local resolver re-implementations", { skip: true }, () => {
882
+ test("consuming app target-root helpers do not reimplement Cargo metadata", () => {
882
883
  const LOCAL_RESOLVER_PATTERN = /function\s+(?:cargoTargetRoot|resolveManagedCargoTarget)\s*\([^)]*\)\s*\{[^}]*cargo[^}]*metadata/s;
883
884
  for (const helperPath of cargoTargetRootHelpers) {
884
885
  const appRoot = path.join(workspace, helperPath.split("/scripts/lib/target-root.mjs")[0]);
@@ -894,6 +895,96 @@ test("consuming app target-root helpers are thin re-export shims, not local reso
894
895
  }
895
896
  });
896
897
 
898
+ const cargoAuthorityRoots = [
899
+ ...cargoTargetDirGuardApps,
900
+ "citadel",
901
+ "heardright",
902
+ "legion",
903
+ "rightsites",
904
+ "rightsuite",
905
+ "sellright",
906
+ "tools/screenright",
907
+ "voiceright",
908
+ "workright",
909
+ ];
910
+ const cargoAuthorityNames = [
911
+ "CARGO_HOME",
912
+ "CARGO_TARGET_DIR",
913
+ "CARGO_BUILD_TARGET_DIR",
914
+ "CARGO_BUILD_BUILD_DIR",
915
+ "CARGO_BUILD_JOBS",
916
+ "CARGO_ENCODED_RUSTFLAGS",
917
+ "RUSTFLAGS",
918
+ "RUSTC_WRAPPER",
919
+ "RUSTC_WORKSPACE_WRAPPER",
920
+ "SCCACHE_DIR",
921
+ "SCCACHE_BASEDIRS",
922
+ "SCCACHE_CACHE_SIZE",
923
+ ];
924
+ const cargoAuthorityExtensions = new Set([".mjs", ".js", ".cjs", ".ts", ".sh", ".ps1", ".py", ".json"]);
925
+
926
+ function findFirstPartyBuildScripts(root) {
927
+ const found = [];
928
+ const visit = (dir) => {
929
+ if (!existsSync(dir)) return;
930
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
931
+ if (entry.isDirectory() && [
932
+ ".agent", ".audit", ".cache", ".git", ".right-release", "bakeoff", "dist", "docs",
933
+ "node_modules", "reference", "target", "tests", "vendor",
934
+ ].includes(entry.name)) continue;
935
+ const full = path.join(dir, entry.name);
936
+ if (entry.isDirectory()) {
937
+ visit(full);
938
+ continue;
939
+ }
940
+ if (!entry.isFile() || !cargoAuthorityExtensions.has(path.extname(entry.name))) continue;
941
+ const relative = path.relative(root, full).replaceAll(path.sep, "/");
942
+ if (/(?:^|\/)(?:test-|[^/]+\.test\.)/.test(relative)) continue;
943
+ if (
944
+ relative.includes("/scripts/")
945
+ || relative.startsWith("scripts/")
946
+ || /(?:^|\/)(?:package\.json|package\.(?:sh|ps1)|right-release\.config\.mjs)$/.test(relative)
947
+ || (!relative.includes("/") && /\.(?:sh|ps1)$/.test(relative))
948
+ ) found.push(full);
949
+ }
950
+ };
951
+ visit(root);
952
+ return found;
953
+ }
954
+
955
+ function executableScriptSource(source) {
956
+ return source
957
+ .replace(/\/\*[\s\S]*?\*\//g, "")
958
+ .split("\n")
959
+ .filter((line) => !/^\s*(?:\/\/|#)/.test(line))
960
+ .join("\n");
961
+ }
962
+
963
+ test("product scripts never override or destroy RightKit-owned Cargo cache state", () => {
964
+ const authorityName = cargoAuthorityNames.join("|");
965
+ const jsMutation = new RegExp(`(?:delete\\s+process\\.env\\.(?:${authorityName})|process\\.env\\.(?:${authorityName})\\s*=|\\b(?:${authorityName})\\s*:)`);
966
+ const shellMutation = new RegExp(`(?:^|\\n)\\s*(?:export\\s+|unset\\s+|env\\s+(?:-[^\\n ]+\\s+)*-u\\s+)?(?:${authorityName})(?:\\s*=|\\b)`);
967
+ const powershellMutation = new RegExp(`\\$env:(?:${authorityName})\\s*=`, "i");
968
+ const cargoClean = /\bcargo\s+clean\b|["']cargo["'][\s\S]{0,400}?["']clean["']/;
969
+
970
+ for (const appRoot of cargoAuthorityRoots) {
971
+ const root = path.join(workspace, appRoot);
972
+ if (!existsSync(root)) continue;
973
+ for (const filePath of findFirstPartyBuildScripts(root)) {
974
+ const source = executableScriptSource(readFileSync(filePath, "utf8"));
975
+ const relative = path.relative(workspace, filePath);
976
+ assert.doesNotMatch(source, jsMutation, `${relative} mutates RightKit-owned Cargo/cache environment`);
977
+ assert.doesNotMatch(source, shellMutation, `${relative} mutates RightKit-owned Cargo/cache environment`);
978
+ assert.doesNotMatch(source, powershellMutation, `${relative} mutates RightKit-owned Cargo/cache environment`);
979
+ assert.doesNotMatch(source, cargoClean, `${relative} destroys reusable Cargo output with cargo clean`);
980
+ assert.doesNotMatch(source, /--target-dir\b/, `${relative} bypasses RightKit target ownership`);
981
+ assert.doesNotMatch(source, /(?:^|[\\/])\.cargo[\\/]bin(?:[\\/]|\b)/i, `${relative} bypasses managed Cargo by injecting a toolchain directory`);
982
+ assert.doesNotMatch(source, /build-guard\.mjs/, `${relative} depends on obsolete product-local build/cache control`);
983
+ }
984
+ assert.equal(existsSync(path.join(root, "scripts", "build-guard.mjs")), false, `${appRoot} must not carry a product-local build guard`);
985
+ }
986
+ });
987
+
897
988
  test("Right Suite has no hosted workflow files", () => {
898
989
  for (const root of ["viewright", "scraperight", "heardright", "mailright", "coderight", "genright", "voiceright", "tools/rightkit"]) {
899
990
  const workflowDir = path.join(workspace, root, ".github", "workflows");
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schema": 2,
3
- "packageManager": "pnpm@11.18.0",
3
+ "packageManager": "pnpm@11.24.0",
4
4
  "npm": {
5
5
  "@rightkit/git": "0.2.0",
6
6
  "@rightkit/legal": "0.2.0",
@@ -14,18 +14,31 @@
14
14
  },
15
15
  "stagedNpm": {
16
16
  "@rightkit/ax": "0.2.0",
17
- "@rightkit/git": "0.2.0",
18
- "@rightkit/hooks": "0.1.0",
19
- "@rightkit/legal": "0.3.0",
17
+ "@rightkit/git": "0.2.1",
18
+ "@rightkit/hooks": "0.1.1",
19
+ "@rightkit/legal": "0.3.1",
20
20
  "@rightkit/legal-ui": "0.1.1",
21
- "@rightkit/license": "0.1.6",
22
- "@rightkit/release": "0.2.67",
23
- "@rightkit/qa": "0.2.0"
21
+ "@rightkit/license": "0.1.7",
22
+ "@rightkit/logs": "0.1.4",
23
+ "@rightkit/platform-ui": "0.1.1",
24
+ "@rightkit/qa": "0.2.1",
25
+ "@rightkit/release": "0.2.68",
26
+ "@rightkit/tauri": "0.1.1",
27
+ "@rightkit/updates": "0.2.4"
24
28
  },
25
29
  "legacyNpm": {
26
30
  "@rightkit/legal-ui": [
27
31
  "0.1.0"
28
32
  ],
33
+ "@rightkit/hooks": [
34
+ "0.1.0"
35
+ ],
36
+ "@rightkit/legal": [
37
+ "0.3.0"
38
+ ],
39
+ "@rightkit/license": [
40
+ "0.1.6"
41
+ ],
29
42
  "@rightkit/release": [
30
43
  "0.2.22",
31
44
  "0.2.29",
@@ -49,10 +62,12 @@
49
62
  "0.2.63",
50
63
  "0.2.64",
51
64
  "0.2.65",
52
- "0.2.66"
65
+ "0.2.66",
66
+ "0.2.67"
53
67
  ],
54
68
  "@rightkit/qa": [
55
- "0.1.0"
69
+ "0.1.0",
70
+ "0.2.0"
56
71
  ]
57
72
  },
58
73
  "cargo": {
@@ -8,7 +8,7 @@
8
8
  "remote": "https://github.com/bogusyogi/viewright.git",
9
9
  "appDir": ".",
10
10
  "revision": "21a4171fa8add2d8114bc5f07498b60d7e8eafe5",
11
- "packageManager": "pnpm@11.18.0",
11
+ "packageManager": "pnpm@11.24.0",
12
12
  "clone": {
13
13
  "command": "git clone --depth 1 --single-branch https://github.com/bogusyogi/viewright.git C:\\Users\\adrds\\AppData\\Local\\Temp\\rightkit-standalone-sBI0iR\\viewright",
14
14
  "status": 0,
@@ -22,7 +22,7 @@ test("standalone verifier clones, installs, doctors from a nested app root, and
22
22
  writeFileSync(path.join(source, "apps", "desktop", "package.json"), JSON.stringify({
23
23
  name: "standalone-fixture",
24
24
  private: true,
25
- packageManager: "pnpm@11.18.0",
25
+ packageManager: "pnpm@11.24.0",
26
26
  scripts: { "release:doctor": "node doctor.mjs" },
27
27
  }), "utf8");
28
28
  writeFileSync(path.join(source, "apps", "desktop", "pnpm-lock.yaml"), "lockfileVersion: '9.0'\nsettings:\n autoInstallPeers: true\n excludeLinksFromLockfile: false\nimporters:\n .: {}\n", "utf8");