@rightkit/release 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-release.mjs CHANGED
@@ -26,6 +26,7 @@ import { assertPrimaryReleaseCheckout, dirtyBuildInputs, resolveReleaseBuildInpu
26
26
  import { commandOutputPortable, releaseEnvironment, resolveReleaseLayout, runBuildStateMachine, verifySealedRelease, watchProgress } from "./release-state.mjs";
27
27
  import { acquireCacheLease, acquireSuiteBuildSlot, applyCachePrune, assertWriteVolumeFloors, inspectCache, inspectWriteVolumes, markCacheEntrySuccessful, planCachePrune, readCachePolicy, resolveCacheLayout, resolveSharedCacheIdentity, resolveSharedCacheRoot } from "./cache-policy.mjs";
28
28
  import { createTargetBridge } from "./target-bridge.mjs";
29
+ import { managedCargoShimOnPath } from "./cargo-contract.mjs";
29
30
  import { assertNsisInPlaceUpgradeContract } from "./nsis-upgrade-contract.mjs";
30
31
  import { buildPathPrefix, collectPreflight, formatPreflight, preflightFailures } from "./preflight.mjs";
31
32
  import { assertCleanSource } from "./source-gate.mjs";
@@ -167,7 +168,13 @@ try {
167
168
  if (signingIdentity) inputHashes[".right-release/signing-identity.json"] = hashFileText(JSON.stringify(signingIdentity));
168
169
  const cargoLock = requiredInputs.find((file) => /Cargo\.lock$/i.test(file));
169
170
  const cargoToml = requiredInputs.find((file) => /Cargo\.toml$/i.test(file));
170
- const managedCargoTarget = process.env.RIGHTKIT_BUILD_BROKER_SOCKET
171
+ // A broker-managed host is not identified by RIGHTKIT_BUILD_BROKER_SOCKET alone:
172
+ // that variable is exported by login shells only, while the managed cargo shim is
173
+ // on PATH for every shell. Detecting on the variable alone made agent (non-login)
174
+ // shells take the Cache V2 branch, bridge src-tauri/target into the release cache,
175
+ // and then lose CARGO_TARGET_DIR to the broker's unconditional override — so the
176
+ // app bundle landed in the broker workspace and packaging failed on an empty cache.
177
+ const managedCargoTarget = (process.env.RIGHTKIT_BUILD_BROKER_SOCKET || managedCargoShimOnPath(process.env))
171
178
  ? resolveManagedCargoTarget(cargoToml)
172
179
  : null;
173
180
  const cacheIdentity = resolveSharedCacheIdentity({ cargoLockPath: cargoLock, cargoTomlPath: cargoToml, rustcVerbose: toolVersions.rustc, targetTriple: target.cargoTarget ?? target.targetTriple ?? target.rustTarget ?? toolVersions.rustHost, profile: target.profile ?? "release" });
@@ -6,6 +6,7 @@ import { execFileSync, spawnSync } from "node:child_process";
6
6
  import test from "node:test";
7
7
  import { fileURLToPath } from "node:url";
8
8
  import { parseCpuSeconds, processGroupCpuSeconds } from "./process-liveness.mjs";
9
+ import { managedCargoShimOnPath } from "./cargo-contract.mjs";
9
10
 
10
11
  const source = readFileSync(path.join(path.dirname(fileURLToPath(import.meta.url)), "build-release.mjs"), "utf8");
11
12
  const build = path.join(path.dirname(fileURLToPath(import.meta.url)), "build-release.mjs");
@@ -114,3 +115,56 @@ test("CPU sampling degrades to the old behaviour rather than inventing a verdict
114
115
  assert.equal(processGroupCpuSeconds(123, { platform: "darwin", run: () => ({ status: 1, stdout: "" }) }), null);
115
116
  assert.equal(processGroupCpuSeconds(123, { platform: "darwin", run: ok("") }), null);
116
117
  });
118
+
119
+ // Regression: the managed-host gate must not depend on RIGHTKIT_BUILD_BROKER_SOCKET
120
+ // alone. That variable is exported by login shells only, while the managed cargo
121
+ // shim is on PATH in every shell — so an agent shell took the Cache V2 branch,
122
+ // bridged src-tauri/target into the release cache, and then lost CARGO_TARGET_DIR
123
+ // to the broker's unconditional override. The app built into the broker workspace
124
+ // and packaging failed on an empty cache before notarize/staple.
125
+ const managedHostDetected = (env, platform, exists) =>
126
+ Boolean(env.RIGHTKIT_BUILD_BROKER_SOCKET || managedCargoShimOnPath(env, platform, exists));
127
+
128
+ test("managed-host gate fires from the managed shim on PATH when no broker socket is exported", () => {
129
+ // The build gate is exactly the dual detection cargo-contract.mjs already uses.
130
+ assert.match(
131
+ source,
132
+ /const managedCargoTarget = \(process\.env\.RIGHTKIT_BUILD_BROKER_SOCKET \|\| managedCargoShimOnPath\(process\.env\)\)\n\s*\? resolveManagedCargoTarget\(cargoToml\)\n\s*: null;/,
133
+ );
134
+ assert.match(source, /import \{ managedCargoShimOnPath \} from "\.\/cargo-contract\.mjs";/);
135
+
136
+ const shim = "/Volumes/D/.rightkit-managed/agent-bin/cargo";
137
+ const exists = (candidate) => candidate === shim;
138
+
139
+ // Agent (non-login) shell: no socket variable, managed shim first on PATH.
140
+ assert.equal(managedHostDetected({ PATH: `${path.dirname(shim)}:/usr/bin` }, "darwin", exists), true);
141
+ // Login shell: unchanged behaviour, with or without the shim resolvable.
142
+ assert.equal(
143
+ managedHostDetected({ PATH: `${path.dirname(shim)}:/usr/bin`, RIGHTKIT_BUILD_BROKER_SOCKET: "/managed/broker.sock" }, "darwin", exists),
144
+ true,
145
+ );
146
+ assert.equal(managedHostDetected({ PATH: "/usr/bin", RIGHTKIT_BUILD_BROKER_SOCKET: "/managed/broker.sock" }, "darwin", () => false), true);
147
+ // Unmanaged host: neither signal, so the Cache V2 branch still owns the build.
148
+ assert.equal(managedHostDetected({ PATH: "/usr/bin:/usr/local/bin" }, "darwin", () => false), false);
149
+ // PATH precedence still decides: an unmanaged toolchain ahead of the shim is unmanaged.
150
+ const both = new Set(["/usr/local/bin/cargo", shim]);
151
+ assert.equal(managedHostDetected({ PATH: `/usr/local/bin:${path.dirname(shim)}` }, "darwin", (c) => both.has(c)), false);
152
+ });
153
+
154
+ test("the managed branch hands the Rust target directory to the broker while the unmanaged branch keeps Cache V2", () => {
155
+ // Managed: CARGO_TARGET_DIR (and the rest of the Cache V2 environment) is deleted
156
+ // from the child environment, so the broker's own override is the only one.
157
+ assert.match(
158
+ source,
159
+ /if \(managedCargoTarget\) \{\n\s*for \(const name of \["CARGO_HOME", "CARGO_TARGET_DIR", "RUSTC_WRAPPER", "SCCACHE_DIR", "SCCACHE_BASEDIRS", "SCCACHE_CACHE_SIZE", "RUSTFLAGS"\]\) \{\n\s*delete env\[name\];/,
160
+ );
161
+ // Managed: no vault build root, and the bridge points at the metadata-reported target.
162
+ assert.match(source, /const buildRoot = managedCargoTarget \? null :/);
163
+ assert.match(source, /target: managedCargoTarget \?\? env\.CARGO_TARGET_DIR,/);
164
+ assert.match(source, /ownedRoot: path\.dirname\(managedCargoTarget \?\? env\.CARGO_TARGET_DIR\),/);
165
+ // The managed target is read from crate metadata through the shim, which is what
166
+ // makes the bridge target equal to the directory the broker actually builds into.
167
+ assert.match(source, /function resolveManagedCargoTarget\(manifestPath\)[\s\S]*?JSON\.parse\(output\)\.target_directory/);
168
+ // Unmanaged (managedCargoTarget === null): Cache V2 still supplies CARGO_TARGET_DIR.
169
+ assert.match(source, /\.\.\.releaseEnvironment\(\{ root: vaultRoot, cacheRoot: sharedCacheRoot,/);
170
+ });
@@ -10,10 +10,27 @@ const CRATES_IO_SOURCES = new Set([
10
10
  ]);
11
11
  const CACHE_V2_ENVIRONMENT = ["CARGO_HOME", "CARGO_TARGET_DIR", "RUSTC_WRAPPER", "SCCACHE_DIR", "SCCACHE_BASEDIRS", "RIGHT_RELEASE_CACHE_OWNER"];
12
12
 
13
- export function isolatedCargoMetadataEnv(cargoHome, env = process.env) {
13
+ export function managedCargoShimOnPath(env = process.env, platform = process.platform, exists = existsSync) {
14
+ const pathApi = platform === "win32" ? path.win32 : path.posix;
15
+ const delimiter = platform === "win32" ? ";" : ":";
16
+ const extensions = platform === "win32"
17
+ ? String(env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean)
18
+ : [""];
19
+ for (const directory of String(env.PATH ?? "").split(delimiter).filter(Boolean)) {
20
+ for (const extension of extensions) {
21
+ const candidate = pathApi.join(directory, `cargo${extension.toLowerCase()}`);
22
+ if (!exists(candidate)) continue;
23
+ return /(?:^|\/)(?:\.rightkit-managed|rightkitmanagedagent)\/agent-bin\/cargo(?:\.[^/]*)?$/i
24
+ .test(candidate.replaceAll("\\", "/"));
25
+ }
26
+ }
27
+ return false;
28
+ }
29
+
30
+ export function isolatedCargoMetadataEnv(cargoHome, env = process.env, platform = process.platform, exists = existsSync) {
14
31
  const metadataEnv = { ...env };
15
32
  for (const name of CACHE_V2_ENVIRONMENT) delete metadataEnv[name];
16
- if (metadataEnv.RIGHTKIT_BUILD_BROKER_SOCKET) return metadataEnv;
33
+ if (metadataEnv.RIGHTKIT_BUILD_BROKER_SOCKET || managedCargoShimOnPath(metadataEnv, platform, exists)) return metadataEnv;
17
34
  return { ...metadataEnv, CARGO_HOME: cargoHome };
18
35
  }
19
36
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rightkit/release",
3
- "version": "0.2.63",
3
+ "version": "0.2.65",
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",
@@ -11,6 +11,7 @@ import {
11
11
  cargoArguments,
12
12
  cargoExecutable,
13
13
  isolatedCargoMetadataEnv,
14
+ managedCargoShimOnPath,
14
15
  validateRightKitCargoContract,
15
16
  } from "./cargo-contract.mjs";
16
17
  import { assertAsrAdapterPair } from "./asr-artifact-adoption.mjs";
@@ -467,12 +468,12 @@ test("RightKit exposes one current version manifest", () => {
467
468
  "@rightkit/legal": "0.3.0",
468
469
  "@rightkit/legal-ui": "0.1.1",
469
470
  "@rightkit/license": "0.1.6",
470
- "@rightkit/release": "0.2.63",
471
+ "@rightkit/release": "0.2.65",
471
472
  "@rightkit/qa": "0.2.0",
472
473
  });
473
474
  assert.deepEqual(versions.legacyNpm, {
474
475
  "@rightkit/legal-ui": ["0.1.0"],
475
- "@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"],
476
+ "@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"],
476
477
  "@rightkit/qa": ["0.1.0"],
477
478
  });
478
479
  assert.ok(
@@ -518,6 +519,36 @@ test("Cargo metadata delegates controlled storage to managed RightKit", () => {
518
519
  });
519
520
  });
520
521
 
522
+ test("Cargo metadata recognizes managed Cargo from PATH without inherited broker environment", () => {
523
+ const macShim = "/Volumes/D/.rightkit-managed/agent-bin/cargo";
524
+ const macEnv = { PATH: `/usr/bin:${path.dirname(macShim)}`, CARGO_HOME: "" };
525
+ assert.equal(managedCargoShimOnPath(macEnv, "darwin", (candidate) => candidate === macShim), true);
526
+ assert.deepEqual(isolatedCargoMetadataEnv("/isolated/cargo", macEnv, "darwin", (candidate) => candidate === macShim), {
527
+ PATH: macEnv.PATH,
528
+ });
529
+
530
+ const windowsShim = "D:\\RightKitManagedAgent\\agent-bin\\cargo.cmd";
531
+ const windowsEnv = {
532
+ PATH: "C:\\Windows\\System32;D:\\RightKitManagedAgent\\agent-bin",
533
+ PATHEXT: ".EXE;.CMD",
534
+ };
535
+ assert.equal(managedCargoShimOnPath(windowsEnv, "win32", (candidate) => candidate.toLowerCase() === windowsShim.toLowerCase()), true);
536
+ assert.deepEqual(isolatedCargoMetadataEnv("D:\\isolated\\cargo", windowsEnv, "win32", (candidate) => candidate.toLowerCase() === windowsShim.toLowerCase()), windowsEnv);
537
+ });
538
+
539
+ test("Cargo metadata respects PATH precedence when unmanaged Cargo resolves first", () => {
540
+ const env = { PATH: "/usr/local/bin:/Volumes/D/.rightkit-managed/agent-bin" };
541
+ const existing = new Set([
542
+ "/usr/local/bin/cargo",
543
+ "/Volumes/D/.rightkit-managed/agent-bin/cargo",
544
+ ]);
545
+ assert.equal(managedCargoShimOnPath(env, "darwin", (candidate) => existing.has(candidate)), false);
546
+ assert.deepEqual(isolatedCargoMetadataEnv("/isolated/cargo", env, "darwin", (candidate) => existing.has(candidate)), {
547
+ PATH: env.PATH,
548
+ CARGO_HOME: "/isolated/cargo",
549
+ });
550
+ });
551
+
521
552
  test("license v2 public vector is identical at every portable consumer boundary", () => {
522
553
  const canonical = readFileSync(
523
554
  path.join(workspace, "tools/rightkit/crates/rightkit-license/test-vectors/license-v2.json"),
@@ -19,7 +19,7 @@
19
19
  "@rightkit/legal": "0.3.0",
20
20
  "@rightkit/legal-ui": "0.1.1",
21
21
  "@rightkit/license": "0.1.6",
22
- "@rightkit/release": "0.2.63",
22
+ "@rightkit/release": "0.2.65",
23
23
  "@rightkit/qa": "0.2.0"
24
24
  },
25
25
  "legacyNpm": {
@@ -45,7 +45,9 @@
45
45
  "0.2.55",
46
46
  "0.2.56",
47
47
  "0.2.61",
48
- "0.2.62"
48
+ "0.2.62",
49
+ "0.2.63",
50
+ "0.2.64"
49
51
  ],
50
52
  "@rightkit/qa": [
51
53
  "0.1.0"