@rightkit/release 0.2.69 → 0.2.70

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.
Files changed (45) hide show
  1. package/package.json +4 -2
  2. package/rightkit-versions.json +3 -2
  3. package/addon-command.test.mjs +0 -54
  4. package/addon-contract.test.mjs +0 -48
  5. package/asr-artifact-adoption.test.mjs +0 -122
  6. package/build-invocation-contract.test.mjs +0 -124
  7. package/build-release.test.mjs +0 -242
  8. package/cache-command.test.mjs +0 -118
  9. package/cache-policy.test.mjs +0 -346
  10. package/cargo-guard.test.mjs +0 -195
  11. package/cargo-target.test.mjs +0 -82
  12. package/create-mac-updater.test.mjs +0 -14
  13. package/github-release.test.mjs +0 -103
  14. package/heavy-command.test.mjs +0 -221
  15. package/legal-contract.test.mjs +0 -151
  16. package/mirror-root-artifact.test.mjs +0 -58
  17. package/model-promote.test.mjs +0 -284
  18. package/notary-auth.test.mjs +0 -31
  19. package/nsis-payload.test.mjs +0 -139
  20. package/nsis-upgrade-contract.test.mjs +0 -57
  21. package/pipeline-normalization-contract.test.mjs +0 -140
  22. package/preflight.test.mjs +0 -123
  23. package/progress-control.test.mjs +0 -56
  24. package/prune-r2.test.mjs +0 -12
  25. package/publish-cargo.test.mjs +0 -43
  26. package/publish-swift.test.mjs +0 -44
  27. package/publish-update.test.mjs +0 -207
  28. package/qa-contract.test.mjs +0 -47
  29. package/registry-parity.test.mjs +0 -30
  30. package/release-cli-contract.test.mjs +0 -119
  31. package/release-invocation.test.mjs +0 -22
  32. package/release-state.test.mjs +0 -395
  33. package/release-token.test.mjs +0 -21
  34. package/release.test.mjs +0 -533
  35. package/right-suite-contract.test.mjs +0 -1011
  36. package/rightapps-register.test.mjs +0 -28
  37. package/runtime-artifact-manifest.test.mjs +0 -128
  38. package/sign-updater.test.mjs +0 -12
  39. package/source-gate.test.mjs +0 -25
  40. package/standalone-clone-evidence.json +0 -32
  41. package/standalone-clone-verify.test.mjs +0 -76
  42. package/suite-doctor.test.mjs +0 -19
  43. package/target-bridge.test.mjs +0 -269
  44. package/tauri-bundle-marker.test.mjs +0 -81
  45. package/upload-large.test.mjs +0 -70
@@ -1,118 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import { existsSync, mkdirSync, mkdtempSync, readFileSync, symlinkSync, writeFileSync } from "node:fs";
3
- import os from "node:os";
4
- import path from "node:path";
5
- import { spawnSync } from "node:child_process";
6
- import test from "node:test";
7
- import { fileURLToPath } from "node:url";
8
-
9
- const root = path.dirname(fileURLToPath(import.meta.url));
10
- const cli = path.join(root, "cli", "right-release.mjs");
11
-
12
- // This suite must exercise the non-broker cache-command path deterministically,
13
- // regardless of whether the machine running the tests is itself broker-managed
14
- // (this workspace's own agent shells are: the managed cargo shim sits on PATH).
15
- // Strip both broker signals from the inherited environment so "unmanaged host"
16
- // tests stay unmanaged; individual tests opt back into broker signals explicitly.
17
- function unmanagedEnv(base = process.env) {
18
- const sanitized = { ...base };
19
- delete sanitized.RIGHTKIT_BUILD_BROKER_SOCKET;
20
- const delimiter = process.platform === "win32" ? ";" : ":";
21
- sanitized.PATH = String(base.PATH ?? "")
22
- .split(delimiter)
23
- .filter((entry) => !/(?:^|[/\\])(?:\.rightkit-managed|rightkitmanagedagent)[/\\]agent-bin$/i.test(entry))
24
- .join(delimiter);
25
- return sanitized;
26
- }
27
-
28
- function run(args, cacheRoot, env = {}) {
29
- return spawnSync(process.execPath, [cli, "cache", ...args], { encoding: "utf8", env: { ...unmanagedEnv(), RIGHT_RELEASE_CACHE_ROOT: cacheRoot, ...env } });
30
- }
31
-
32
- test("cache status has human and schema-stable JSON output", () => {
33
- const cacheRoot = mkdtempSync(path.join(os.tmpdir(), "rightkit-cache-command-"));
34
- const human = run(["status"], cacheRoot);
35
- const json = run(["status", "--json"], cacheRoot);
36
- assert.equal(human.status, 0, human.stderr);
37
- assert.match(human.stdout, /RightKit cache root:/);
38
- assert.match(human.stdout, /Backups: 0 bytes/);
39
- assert.equal(json.status, 0, json.stderr);
40
- assert.equal(JSON.parse(json.stdout).schema, 1);
41
- });
42
-
43
- test("cache prune defaults to dry-run and only apply can mutate an admitted temporary entry", () => {
44
- const cacheRoot = mkdtempSync(path.join(os.tmpdir(), "rightkit-cache-command-"));
45
- const target = path.join(cacheRoot, "targets", "mac", "fixture", "1111111111111111");
46
- mkdirSync(target, { recursive: true });
47
- writeFileSync(path.join(target, "payload"), "payload");
48
- writeFileSync(path.join(target, ".rightkit-cache-entry.json"), JSON.stringify({ schema: 1, id: "target:mac:fixture:1111111111111111", kind: "release", platform: "mac", architecture: "aarch64", app: "fixture", fingerprint: "1111111111111111", createdAt: "2020-01-01T00:00:00.000Z", lastUsedAt: "2020-01-01T00:00:00.000Z", lastSuccessfulBuildAt: "2020-01-01T00:00:00.000Z", toolchain: {} }));
49
- const dry = run(["prune", "--dry-run", "--json"], cacheRoot);
50
- assert.equal(dry.status, 0, dry.stderr);
51
- assert.equal(JSON.parse(dry.stdout).dryRun, true);
52
- assert.equal(run(["prune", "--apply", "--json"], cacheRoot).status, 0);
53
- });
54
-
55
- test("cache rejects unknown commands and migration requires a config", () => {
56
- const cacheRoot = mkdtempSync(path.join(os.tmpdir(), "rightkit-cache-command-"));
57
- assert.notEqual(run(["unknown"], cacheRoot).status, 0);
58
- assert.notEqual(run(["migrate"], cacheRoot).status, 0);
59
- });
60
-
61
- test("status and prune dry-run do not create a cache root or lock file", () => {
62
- const parent = mkdtempSync(path.join(os.tmpdir(), "rightkit-cache-command-"));
63
- const cacheRoot = path.join(parent, "never-created");
64
- assert.equal(run(["status", "--json"], cacheRoot).status, 0);
65
- assert.equal(run(["prune", "--dry-run", "--json"], cacheRoot).status, 0);
66
- assert.equal(existsSync(cacheRoot), false);
67
- });
68
-
69
- test("cache migrate uses the repository vault Cargo home, not an app-local lookalike", () => {
70
- const sandbox = mkdtempSync(path.join(os.tmpdir(), "rightkit-cache-command-"));
71
- const repo = path.join(sandbox, "repo"); const app = path.join(repo, "app"); const cacheRoot = path.join(sandbox, "shared-cache");
72
- const config = path.join(app, "right-release.config.mjs"); const fingerprint = "abcdef0123456789";
73
- const target = path.join(repo, ".right-release", "cache", "cargo-target", "mac", fingerprint);
74
- const vaultCargoHome = path.join(repo, ".right-release", "cache", "cargo-home");
75
- const appCargoHome = path.join(app, ".right-release", "cache", "cargo-home");
76
- mkdirSync(path.join(repo, ".git"), { recursive: true });
77
- mkdirSync(path.dirname(config), { recursive: true });
78
- writeFileSync(config, "export default { app: 'fixture', targets: { mac: { targetTriple: 'aarch64-apple-darwin' } } };\n");
79
- mkdirSync(path.join(app, "src-tauri"), { recursive: true });
80
- writeFileSync(path.join(app, "src-tauri", "Cargo.lock"), "fixture-lock\n");
81
- writeFileSync(path.join(app, "src-tauri", "Cargo.toml"), "[package]\nname = 'fixture'\n");
82
- mkdirSync(target, { recursive: true }); writeFileSync(path.join(target, "artifact"), "target");
83
- symlinkSync(target, path.join(app, "src-tauri", "target"));
84
- mkdirSync(vaultCargoHome, { recursive: true }); writeFileSync(path.join(vaultCargoHome, "registry"), "vault");
85
- mkdirSync(appCargoHome, { recursive: true }); writeFileSync(path.join(appCargoHome, "registry"), "app-local");
86
- const result = run(["migrate", "--config", config, "--apply", "--json"], cacheRoot, { RIGHT_RELEASE_PLATFORM: "mac", RIGHT_RELEASE_RUSTC_VERBOSE: "rustc 1.91.0\nhost: aarch64-apple-darwin", RIGHT_RELEASE_CACHE_KEY: fingerprint });
87
- assert.equal(result.status, 0, result.stderr);
88
- assert.equal(JSON.parse(result.stdout).cargoHomeMoved, true);
89
- assert.equal(readFileSync(path.join(cacheRoot, "cargo-home", "registry"), "utf8"), "vault");
90
- assert.equal(readFileSync(path.join(appCargoHome, "registry"), "utf8"), "app-local");
91
- });
92
-
93
- test("status, prune, and migrate short-circuit cleanly on a broker-managed host without proposing a repair", () => {
94
- const cacheRoot = mkdtempSync(path.join(os.tmpdir(), "rightkit-cache-command-"));
95
- const brokerEnv = { RIGHTKIT_BUILD_BROKER_SOCKET: "/managed/broker.sock" };
96
-
97
- const status = run(["status", "--json"], cacheRoot, brokerEnv);
98
- assert.equal(status.status, 0, status.stderr);
99
- const statusBody = JSON.parse(status.stdout);
100
- assert.equal(statusBody.brokerManaged, true);
101
- assert.match(statusBody.message, /broker-managed host/);
102
- assert.doesNotMatch(status.stdout + status.stderr, /warn|repair|corrupt/i);
103
-
104
- const prune = run(["prune", "--dry-run", "--json"], cacheRoot, brokerEnv);
105
- assert.equal(prune.status, 0, prune.stderr);
106
- assert.equal(JSON.parse(prune.stdout).brokerManaged, true);
107
-
108
- // migrate short-circuits before even requiring --config, since a broker host has
109
- // nothing under Cache V2's target layout for it to migrate into.
110
- const migrate = run(["migrate", "--json"], cacheRoot, brokerEnv);
111
- assert.equal(migrate.status, 0, migrate.stderr);
112
- assert.equal(JSON.parse(migrate.stdout).brokerManaged, true);
113
-
114
- // Non-broker host: unchanged behaviour.
115
- const unmanaged = run(["status", "--json"], cacheRoot);
116
- assert.equal(unmanaged.status, 0, unmanaged.stderr);
117
- assert.equal(JSON.parse(unmanaged.stdout).brokerManaged, undefined);
118
- });
@@ -1,346 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import { existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, rmSync, symlinkSync, utimesSync, writeFileSync } from "node:fs";
3
- import os from "node:os";
4
- import path from "node:path";
5
- import test from "node:test";
6
- import { spawn } from "node:child_process";
7
- import { once } from "node:events";
8
-
9
- import {
10
- CACHE_SCHEMA,
11
- DEFAULT_BACKUP_RETENTION_DAYS,
12
- DEFAULT_DESIRED_FREE_BYTES,
13
- DEFAULT_HARD_FREE_BYTES,
14
- DEFAULT_SCCACHE_MAX_BYTES,
15
- DEFAULT_TARGET_MAX_BYTES,
16
- acquireCacheLease,
17
- acquireSuiteBuildSlot,
18
- applyCachePrune,
19
- assertWriteVolumeFloors,
20
- ensureCacheEntry,
21
- inspectCache,
22
- inspectWriteVolumes,
23
- markCacheEntrySuccessful,
24
- migrateLegacyCache,
25
- planCachePrune,
26
- readCachePolicy,
27
- resolveCacheLayout,
28
- resolveSharedCacheIdentity,
29
- resolveSharedCacheRoot,
30
- resolveStableRoot,
31
- } from "./cache-policy.mjs";
32
-
33
- function root() { return path.join(os.tmpdir(), `rightkit-cache-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`); }
34
- function layout(dir, fingerprint = "a1b2c3d4e5f60708") {
35
- return resolveCacheLayout({ cacheRoot: dir, platform: "mac", architecture: "aarch64", app: "fixture", fingerprint, kind: "release" });
36
- }
37
-
38
- function migrationFixture() {
39
- const base = root();
40
- const fingerprint = "abcdef0123456789";
41
- const legacyRoot = path.join(base, "repo", "app", "src-tauri", "target");
42
- const legacyTargetRoot = path.join(base, "repo", ".right-release", "cache", "cargo-target", "mac", fingerprint);
43
- const legacyCargoHome = path.join(base, "repo", ".right-release", "cache", "cargo-home");
44
- mkdirSync(legacyTargetRoot, { recursive: true });
45
- writeFileSync(path.join(legacyTargetRoot, "artifact"), "legacy-target");
46
- mkdirSync(legacyCargoHome, { recursive: true });
47
- writeFileSync(path.join(legacyCargoHome, "registry"), "legacy-cargo-home");
48
- mkdirSync(path.dirname(legacyRoot), { recursive: true });
49
- symlinkSync(legacyTargetRoot, legacyRoot);
50
- const sharedCacheRoot = path.join(base, "shared-cache");
51
- return {
52
- fingerprint,
53
- legacyRoot,
54
- legacyTargetRoot,
55
- legacyCargoHome,
56
- layout: layout(sharedCacheRoot, fingerprint),
57
- app: "fixture",
58
- legacyReleaseLockPath: path.join(base, "repo", ".right-release", "locks", "mac.lock.json"),
59
- };
60
- }
61
-
62
- test("cache roots are platform-native and overrides must be absolute", () => {
63
- assert.throws(() => resolveSharedCacheRoot({ platform: "mac", home: "/Users/test", env: {} }), /RIGHT_RELEASE_CACHE_ROOT is required/);
64
- assert.equal(resolveSharedCacheRoot({ platform: "win", env: { LOCALAPPDATA: "C:/Users/test/AppData/Local" } }), "C:\\Users\\test\\AppData\\Local\\RightSuite\\Cache\\release");
65
- assert.equal(resolveSharedCacheRoot({ platform: "linux", home: "/home/test", env: {} }), "/home/test/.cache/rightsuite/release");
66
- assert.equal(resolveSharedCacheRoot({ platform: "mac", env: { RIGHT_RELEASE_CACHE_ROOT: "/tmp/cache" } }), "/tmp/cache");
67
- assert.throws(() => resolveSharedCacheRoot({ platform: "mac", env: { RIGHT_RELEASE_CACHE_ROOT: "relative" } }), /absolute/i);
68
- });
69
-
70
- test("shared cache root ignores XDG_CACHE_HOME regardless of value", () => {
71
- assert.equal(
72
- resolveSharedCacheRoot({ platform: "linux", env: { XDG_CACHE_HOME: "/one/path" }, home: "/home/u" }),
73
- resolveSharedCacheRoot({ platform: "linux", env: {}, home: "/home/u" }),
74
- );
75
- assert.equal(resolveSharedCacheRoot({ platform: "linux", env: { XDG_CACHE_HOME: "/one/path" }, home: "/home/u" }), "/home/u/.cache/rightsuite/release");
76
- // resolveSharedCacheRoot no longer accepts an xdgCacheHome option; passing one has no effect.
77
- assert.equal(
78
- resolveSharedCacheRoot({ platform: "linux", env: {}, home: "/home/u", xdgCacheHome: "/should/be/ignored" }),
79
- "/home/u/.cache/rightsuite/release",
80
- );
81
- });
82
-
83
- test("resolveStableRoot: explicit override wins, win32 requires the env var, other platforms ignore arbitrary env", () => {
84
- const opts = {
85
- darwinSegments: ["Library", "Caches", "Example"],
86
- win32Segments: ["Example"],
87
- otherSegments: ["example"],
88
- };
89
- assert.equal(
90
- resolveStableRoot({ ...opts, platform: "linux", explicitOverride: "/explicit/path", overrideName: "EXAMPLE_ROOT", env: { EXAMPLE_ROOT: "ignored-by-caller-arg" } }),
91
- "/explicit/path",
92
- );
93
- assert.throws(
94
- () => resolveStableRoot({ ...opts, platform: "linux", explicitOverride: "relative/path", overrideName: "EXAMPLE_ROOT" }),
95
- /EXAMPLE_ROOT must be an absolute path/,
96
- );
97
- assert.throws(
98
- () => resolveStableRoot({ ...opts, platform: "win32", env: {} }),
99
- /LOCALAPPDATA must be an absolute path for RightSuite/,
100
- );
101
- assert.equal(
102
- resolveStableRoot({ ...opts, platform: "win32", env: { LOCALAPPDATA: "C:/Users/test/AppData/Local" } }),
103
- "C:\\Users\\test\\AppData\\Local\\Example",
104
- );
105
- assert.equal(
106
- resolveStableRoot({ ...opts, platform: "linux", env: { XDG_CACHE_HOME: "/ignored", npm_config_cache: "/ignored" }, home: "/home/u" }),
107
- "/home/u/.cache/example",
108
- );
109
- });
110
-
111
- test("layout isolates app, platform, kind, and fingerprint", () => {
112
- const base = root();
113
- const release = layout(base);
114
- const testLayout = resolveCacheLayout({ cacheRoot: base, platform: "mac", architecture: "aarch64", app: "other", fingerprint: "a1b2c3d4e5f60708", kind: "test" });
115
- assert.match(release.targetDir, /targets[\\/]mac[\\/]fixture[\\/]a1b2c3d4e5f60708$/);
116
- assert.match(testLayout.targetDir, /test-targets[\\/]mac[\\/]other[\\/]a1b2c3d4e5f60708$/);
117
- assert.notEqual(release.targetDir, testLayout.targetDir);
118
- });
119
-
120
- test("policy defaults are the approved target, compiler, desired and hard limits", () => {
121
- const policy = readCachePolicy({});
122
- assert.equal(CACHE_SCHEMA, 1);
123
- assert.equal(policy.targetMaxBytes, DEFAULT_TARGET_MAX_BYTES);
124
- assert.equal(policy.sccacheMaxBytes, DEFAULT_SCCACHE_MAX_BYTES);
125
- assert.equal(policy.desiredFreeBytes, DEFAULT_DESIRED_FREE_BYTES);
126
- assert.equal(policy.hardFreeBytes, DEFAULT_HARD_FREE_BYTES);
127
- assert.equal(policy.backupRetentionDays, DEFAULT_BACKUP_RETENTION_DAYS);
128
- });
129
-
130
- test("pruning expires only old direct backup directories", () => {
131
- const base = root();
132
- const old = path.join(base, "manual-backups", "old-target");
133
- const recent = path.join(base, "recovery", "recent-target");
134
- const outside = root();
135
- mkdirSync(old, { recursive: true }); writeFileSync(path.join(old, "artifact"), "old");
136
- mkdirSync(recent, { recursive: true }); writeFileSync(path.join(recent, "artifact"), "recent");
137
- mkdirSync(outside, { recursive: true });
138
- mkdirSync(path.join(base, "legacy-backups"), { recursive: true });
139
- symlinkSync(outside, path.join(base, "legacy-backups", "escape"));
140
- utimesSync(old, new Date("2026-07-01T00:00:00Z"), new Date("2026-07-01T00:00:00Z"));
141
- utimesSync(recent, new Date("2026-08-07T00:00:00Z"), new Date("2026-08-07T00:00:00Z"));
142
- const snapshot = inspectCache({ cacheRoot: base, now: new Date("2026-08-08T00:00:00Z"), statfs: () => ({ bavail: 100, bsize: 1 }) });
143
- assert.deepEqual(snapshot.backups.map((entry) => entry.id).sort(), ["backup:manual-backups:old-target", "backup:recovery:recent-target"]);
144
- const plan = planCachePrune({ snapshot, policy: { ...readCachePolicy({}), targetMaxBytes: 1, desiredFreeBytes: 0 } });
145
- assert.deepEqual(plan.candidateIds, ["backup:manual-backups:old-target"]);
146
- const result = applyCachePrune({ plan, cacheRoot: base, dryRun: false });
147
- assert.deepEqual(result.removedIds, ["backup:manual-backups:old-target"]);
148
- assert.equal(existsSync(old), false);
149
- assert.equal(existsSync(recent), true);
150
- assert.equal(existsSync(outside), true);
151
- });
152
-
153
- test("entry metadata is atomic, complete, and updates success only after requested", () => {
154
- const entry = layout(root());
155
- ensureCacheEntry({ layout: entry, metadata: { toolchain: { cargo: "cargo 1", rustc: "rustc 1", host: "host" } }, now: new Date("2026-07-21T00:00:00Z") });
156
- const created = JSON.parse(readFileSync(entry.markerPath, "utf8"));
157
- assert.equal(created.schema, CACHE_SCHEMA);
158
- assert.equal(created.id, "target:mac:fixture:a1b2c3d4e5f60708");
159
- assert.equal(created.lastSuccessfulBuildAt, null);
160
- markCacheEntrySuccessful({ layout: entry, now: new Date("2026-07-21T01:00:00Z") });
161
- assert.equal(JSON.parse(readFileSync(entry.markerPath, "utf8")).lastSuccessfulBuildAt, "2026-07-21T01:00:00.000Z");
162
- });
163
-
164
- test("entry leases protect live entries, reclaim stale owners, and suite slots time out without a fallback", () => {
165
- const entry = layout(root());
166
- ensureCacheEntry({ layout: entry });
167
- const lease = acquireCacheLease({ layout: entry, pid: process.pid, argv: ["test"] });
168
- assert.throws(() => acquireCacheLease({ layout: entry, pid: process.pid + 100000, argv: ["second"] }), /lease.*active/i);
169
- lease.release();
170
- writeFileSync(entry.leasePath, JSON.stringify({ pid: 99999999, createdAt: "2000-01-01T00:00:00.000Z" }));
171
- const reclaimed = acquireCacheLease({ layout: entry, pid: process.pid, argv: ["reclaimed"] });
172
- reclaimed.release();
173
- const slot = acquireSuiteBuildSlot({ layout: entry, pid: process.pid, argv: ["holder"], waitMs: 0 });
174
- assert.throws(() => acquireSuiteBuildSlot({ layout: entry, pid: process.pid + 100000, argv: ["waiter"], waitMs: 0, sleep: () => {} }), /suite build slot.*active|timed out/i);
175
- slot.release();
176
- });
177
-
178
- test("build and migration derive identical native-feature cache fingerprints", () => {
179
- const base = root(); const cargoLock = path.join(base, "Cargo.lock"); const cargoToml = path.join(base, "Cargo.toml");
180
- mkdirSync(base, { recursive: true });
181
- writeFileSync(cargoLock, "[[package]]\nname = 'fixture'\n");
182
- writeFileSync(cargoToml, "[dependencies]\nrusqlite = { version = '1', features = [\"bundled-sqlcipher\"] }\n");
183
- const inputs = { cargoLockPath: cargoLock, cargoTomlPath: cargoToml, rustcVerbose: "rustc 1.91.0\nhost: aarch64-apple-darwin", targetTriple: "aarch64-apple-darwin", profile: "release" };
184
- const build = resolveSharedCacheIdentity(inputs);
185
- const migration = resolveSharedCacheIdentity(inputs);
186
- assert.equal(build.fingerprint, migration.fingerprint);
187
- assert.notEqual(build.fingerprint, resolveSharedCacheIdentity({ ...inputs, features: [] }).fingerprint);
188
- });
189
-
190
- test("cache identity ignores local workspace version bumps but keeps registry dependency identity", () => {
191
- const base = root();
192
- const cargoLock = path.join(base, "Cargo.lock");
193
- mkdirSync(base, { recursive: true });
194
- const lock = (appVersion, depVersion, checksum) => `version = 4\n\n[[package]]\nname = "fixture"\nversion = "${appVersion}"\ndependencies = [\n "serde",\n]\n\n[[package]]\nname = "serde"\nversion = "${depVersion}"\nsource = "registry+https://github.com/rust-lang/crates.io-index"\nchecksum = "${checksum}"\n`;
195
- const inputs = { cargoLockPath: cargoLock, rustcVerbose: "rustc 1.91.0\nhost: aarch64-apple-darwin", targetTriple: "aarch64-apple-darwin" };
196
-
197
- writeFileSync(cargoLock, lock("1.0.0", "1.0.0", "aaa"));
198
- const original = resolveSharedCacheIdentity(inputs);
199
- writeFileSync(cargoLock, lock("1.0.1", "1.0.0", "aaa"));
200
- assert.equal(resolveSharedCacheIdentity(inputs).fingerprint, original.fingerprint);
201
- writeFileSync(cargoLock, lock("1.0.1", "1.0.1", "bbb"));
202
- assert.notEqual(resolveSharedCacheIdentity(inputs).fingerprint, original.fingerprint);
203
- });
204
-
205
- test("migration moves an actual src-tauri target symlink and keeps dry-run physically pure", () => {
206
- const fixture = migrationFixture();
207
- const preview = migrateLegacyCache({ ...fixture, dryRun: true });
208
- assert.equal(preview.moved, false);
209
- assert.equal(existsSync(fixture.layout.cacheRoot), false);
210
- assert.equal(lstatSync(fixture.legacyRoot).isSymbolicLink(), true);
211
- const result = migrateLegacyCache({ ...fixture, dryRun: false });
212
- assert.equal(result.moved, true);
213
- assert.equal(existsSync(fixture.legacyRoot), false);
214
- assert.equal(readFileSync(path.join(fixture.layout.targetDir, "artifact"), "utf8"), "legacy-target");
215
- assert.equal(readFileSync(path.join(fixture.layout.cargoHome, "registry"), "utf8"), "legacy-cargo-home");
216
- });
217
-
218
- test("migration rejects target symlinks outside the exact derived fingerprint directory", () => {
219
- const fixture = migrationFixture();
220
- const other = path.join(path.dirname(fixture.legacyTargetRoot), "other-fingerprint");
221
- mkdirSync(other, { recursive: true });
222
- symlinkSync(other, `${fixture.legacyRoot}-wrong`);
223
- assert.throws(() => migrateLegacyCache({ ...fixture, legacyRoot: `${fixture.legacyRoot}-wrong`, dryRun: true }), /unexpected target link/i);
224
- symlinkSync(path.join(other, "missing"), `${fixture.legacyRoot}-dangling`);
225
- assert.throws(() => migrateLegacyCache({ ...fixture, legacyRoot: `${fixture.legacyRoot}-dangling`, dryRun: true }), /unexpected target link/i);
226
- });
227
-
228
- test("migration rolls back target, Cargo home, and src-tauri link after every injected post-move failure", () => {
229
- for (const failureAt of ["after-target-move", "after-entry-marker", "after-cargo-home-move", "before-receipt", "receipt-write"]) {
230
- const fixture = migrationFixture();
231
- assert.throws(() => migrateLegacyCache({ ...fixture, dryRun: false, onPostMove: (step) => { if (step === failureAt) throw new Error(`injected ${step}`); }, writeMigrationReceipt: failureAt === "receipt-write" ? () => { throw new Error("injected receipt write"); } : undefined }), /injected/);
232
- assert.equal(readFileSync(path.join(fixture.legacyTargetRoot, "artifact"), "utf8"), "legacy-target", failureAt);
233
- assert.equal(realpathSync(fixture.legacyRoot), realpathSync(fixture.legacyTargetRoot), failureAt);
234
- assert.equal(lstatSync(fixture.legacyRoot).isSymbolicLink(), true, failureAt);
235
- assert.equal(readFileSync(path.join(fixture.legacyCargoHome, "registry"), "utf8"), "legacy-cargo-home", failureAt);
236
- assert.equal(existsSync(fixture.layout.targetDir), false, failureAt);
237
- assert.equal(existsSync(fixture.layout.cargoHome), false, failureAt);
238
- }
239
- });
240
-
241
- test("a competing suite build slot blocks migration before it moves a legacy cache", () => {
242
- const fixture = migrationFixture();
243
- const slot = acquireSuiteBuildSlot({ layout: fixture.layout, pid: process.pid, argv: ["build"], waitMs: 0 });
244
- try {
245
- assert.throws(() => migrateLegacyCache({ ...fixture, dryRun: false, waitMs: 0 }), /suite build slot.*active|timed out/i);
246
- assert.equal(realpathSync(fixture.legacyRoot), realpathSync(fixture.legacyTargetRoot));
247
- assert.equal(existsSync(fixture.layout.targetDir), false);
248
- } finally { slot.release(); }
249
- });
250
-
251
- test("a live shared build lease blocks migration during in-lock revalidation", () => {
252
- const fixture = migrationFixture();
253
- mkdirSync(fixture.layout.leasesDir, { recursive: true });
254
- writeFileSync(path.join(fixture.layout.leasesDir, "another-build.json"), JSON.stringify({ pid: process.pid, createdAt: new Date().toISOString() }));
255
- assert.throws(() => migrateLegacyCache({ ...fixture, dryRun: false }), /shared entry lease is live/i);
256
- assert.equal(realpathSync(fixture.legacyRoot), realpathSync(fixture.legacyTargetRoot));
257
- assert.equal(existsSync(fixture.layout.targetDir), false);
258
- assert.equal(existsSync(fixture.layout.gcLockPath), false);
259
- assert.equal(existsSync(fixture.layout.suiteSlotPath), false);
260
- });
261
-
262
- test("a live legacy release lock blocks migration during in-lock revalidation", () => {
263
- const fixture = migrationFixture();
264
- mkdirSync(path.dirname(fixture.legacyReleaseLockPath), { recursive: true });
265
- writeFileSync(fixture.legacyReleaseLockPath, JSON.stringify({ pid: process.pid }));
266
- assert.throws(() => migrateLegacyCache({ ...fixture, dryRun: false }), /legacy release lock is live/i);
267
- assert.equal(realpathSync(fixture.legacyRoot), realpathSync(fixture.legacyTargetRoot));
268
- assert.equal(existsSync(fixture.layout.targetDir), false);
269
- assert.equal(existsSync(fixture.layout.gcLockPath), false);
270
- assert.equal(existsSync(fixture.layout.suiteSlotPath), false);
271
- });
272
-
273
- test("pruning is oldest successful use first and dry-run equals apply candidates", () => {
274
- const base = root();
275
- const older = layout(base, "1111111111111111");
276
- const newer = layout(base, "2222222222222222");
277
- for (const [entry, stamp] of [[older, "2026-01-01T00:00:00.000Z"], [newer, "2026-02-01T00:00:00.000Z"]]) {
278
- ensureCacheEntry({ layout: entry, now: new Date(stamp) });
279
- writeFileSync(path.join(entry.targetDir, "payload"), "x".repeat(32));
280
- markCacheEntrySuccessful({ layout: entry, now: new Date(stamp) });
281
- }
282
- const snapshot = { cacheRoot: base, targetBytes: 64, entries: [
283
- { id: newer.id, dir: newer.targetDir, bytes: 32, marker: JSON.parse(readFileSync(newer.markerPath)), leased: false },
284
- { id: older.id, dir: older.targetDir, bytes: 32, marker: JSON.parse(readFileSync(older.markerPath)), leased: false },
285
- ] };
286
- const plan = planCachePrune({ snapshot, policy: { targetMaxBytes: 31, desiredFreeBytes: 0 }, protectedEntryIds: new Set() });
287
- assert.deepEqual(plan.candidates.map((x) => x.id), [older.id, newer.id]);
288
- assert.deepEqual(applyCachePrune({ plan, cacheRoot: base, dryRun: true }).candidateIds, applyCachePrune({ plan, cacheRoot: base, dryRun: false }).candidateIds);
289
- assert.equal(existsSync(older.targetDir), false);
290
- assert.equal(existsSync(newer.targetDir), false);
291
- });
292
-
293
- test("pruning fails closed for malformed markers, symlink escapes, and cache root", () => {
294
- const base = root();
295
- const safe = layout(base);
296
- mkdirSync(safe.targetDir, { recursive: true });
297
- writeFileSync(safe.markerPath, "not-json");
298
- const outside = root(); mkdirSync(outside, { recursive: true });
299
- const link = layout(base, "2222222222222222"); mkdirSync(path.dirname(link.targetDir), { recursive: true }); symlinkSync(outside, link.targetDir);
300
- const plan = { candidates: [
301
- { id: "bad", dir: safe.targetDir, bytes: 1 },
302
- { id: "link", dir: link.targetDir, bytes: 1 },
303
- { id: "root", dir: base, bytes: 1 },
304
- ] };
305
- const result = applyCachePrune({ plan, cacheRoot: base, dryRun: false });
306
- assert.deepEqual(result.removedIds, []);
307
- assert.equal(existsSync(safe.targetDir), true);
308
- assert.equal(lstatSync(link.targetDir).isSymbolicLink(), true);
309
- });
310
-
311
- test("entry creation rejects a symlinked cache ancestor before writing outside the cache root", () => {
312
- const parent = root(); const outside = root(); mkdirSync(parent, { recursive: true }); mkdirSync(outside, { recursive: true });
313
- symlinkSync(outside, path.join(parent, "cache"));
314
- const unsafe = resolveCacheLayout({ cacheRoot: path.join(parent, "cache"), platform: "mac", architecture: "aarch64", app: "fixture", fingerprint: "3333333333333333", kind: "release" });
315
- assert.throws(() => ensureCacheEntry({ layout: unsafe }), /symlink|unsafe/i);
316
- assert.equal(existsSync(path.join(outside, "targets")), false);
317
- });
318
-
319
- test("prune takes the global GC lock and blocks a racing lease before deletion", () => {
320
- const base = root(); const entry = layout(base, "4444444444444444"); ensureCacheEntry({ layout: entry }); writeFileSync(path.join(entry.targetDir, "payload"), "x");
321
- const plan = { cacheRoot: base, candidates: [{ id: entry.id, dir: entry.targetDir, bytes: 1 }] };
322
- assert.throws(() => applyCachePrune({ plan, cacheRoot: base, dryRun: false, beforeDelete: () => acquireCacheLease({ layout: entry }) }), /global GC lock active/);
323
- assert.equal(existsSync(entry.targetDir), true);
324
- assert.equal(existsSync(path.join(base, "gc", "gc.lock.json")), false);
325
- });
326
-
327
- test("a real child-process lease acquired after planning excludes its entry from prune", async () => {
328
- const base = root(); const entry = layout(base, "5555555555555555"); ensureCacheEntry({ layout: entry }); writeFileSync(path.join(entry.targetDir, "payload"), "x");
329
- const plan = { cacheRoot: base, candidates: [{ id: entry.id, dir: entry.targetDir, bytes: 1 }] };
330
- const moduleUrl = new URL("./cache-policy.mjs", import.meta.url).href;
331
- const child = spawn(process.execPath, ["--input-type=module", "--eval", `import { resolveCacheLayout, acquireCacheLease } from ${JSON.stringify(moduleUrl)}; const l=resolveCacheLayout({cacheRoot:process.argv[1],platform:'mac',architecture:'aarch64',app:'fixture',fingerprint:'5555555555555555',kind:'release'}); const lease=acquireCacheLease({layout:l}); console.log('ready'); setTimeout(()=>lease.release(),500);`, base], { stdio: ["ignore", "pipe", "pipe"] });
332
- await once(child.stdout, "data");
333
- const result = applyCachePrune({ plan, cacheRoot: base, dryRun: false });
334
- assert.deepEqual(result.removedIds, []);
335
- assert.equal(existsSync(entry.targetDir), true);
336
- await once(child, "exit");
337
- });
338
-
339
- test("write-volume inspection deduplicates same devices and enforces both hard floors", () => {
340
- const statfs = (dir) => ({ bsize: 1024, bavail: dir.includes("cache") ? 20 : 30, dev: dir.includes("same") ? 1 : dir.includes("cache") ? 2 : 3 });
341
- const same = inspectWriteVolumes({ repoRoot: "/same/repo", vaultRoot: "/same/vault", cacheRoot: "/same/cache", statfs });
342
- assert.equal(same.length, 1);
343
- const distinct = inspectWriteVolumes({ repoRoot: "/repo", vaultRoot: "/repo", cacheRoot: "/cache", statfs });
344
- assert.equal(distinct.length, 2);
345
- assert.throws(() => assertWriteVolumeFloors({ volumes: distinct, policy: { hardFreeBytes: 25 * 1024 }, configuredRepoMinimumBytes: 25 * 1024 }), /cache|repo/i);
346
- });