@rightkit/release 0.2.68 → 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 (46) hide show
  1. package/cli/right-release.mjs +0 -0
  2. package/package.json +13 -9
  3. package/release.mjs +47 -14
  4. package/rightkit-versions.json +4 -2
  5. package/addon-command.test.mjs +0 -54
  6. package/addon-contract.test.mjs +0 -48
  7. package/asr-artifact-adoption.test.mjs +0 -122
  8. package/build-invocation-contract.test.mjs +0 -124
  9. package/build-release.test.mjs +0 -242
  10. package/cache-command.test.mjs +0 -118
  11. package/cache-policy.test.mjs +0 -346
  12. package/cargo-guard.test.mjs +0 -195
  13. package/cargo-target.test.mjs +0 -82
  14. package/create-mac-updater.test.mjs +0 -14
  15. package/github-release.test.mjs +0 -103
  16. package/heavy-command.test.mjs +0 -221
  17. package/legal-contract.test.mjs +0 -151
  18. package/mirror-root-artifact.test.mjs +0 -58
  19. package/model-promote.test.mjs +0 -284
  20. package/notary-auth.test.mjs +0 -31
  21. package/nsis-payload.test.mjs +0 -139
  22. package/nsis-upgrade-contract.test.mjs +0 -57
  23. package/preflight.test.mjs +0 -123
  24. package/progress-control.test.mjs +0 -56
  25. package/prune-r2.test.mjs +0 -12
  26. package/publish-cargo.test.mjs +0 -43
  27. package/publish-swift.test.mjs +0 -44
  28. package/publish-update.test.mjs +0 -207
  29. package/qa-contract.test.mjs +0 -47
  30. package/registry-parity.test.mjs +0 -30
  31. package/release-cli-contract.test.mjs +0 -119
  32. package/release-invocation.test.mjs +0 -22
  33. package/release-state.test.mjs +0 -395
  34. package/release-token.test.mjs +0 -21
  35. package/release.test.mjs +0 -477
  36. package/right-suite-contract.test.mjs +0 -1007
  37. package/rightapps-register.test.mjs +0 -28
  38. package/runtime-artifact-manifest.test.mjs +0 -128
  39. package/sign-updater.test.mjs +0 -12
  40. package/source-gate.test.mjs +0 -25
  41. package/standalone-clone-evidence.json +0 -32
  42. package/standalone-clone-verify.test.mjs +0 -76
  43. package/suite-doctor.test.mjs +0 -19
  44. package/target-bridge.test.mjs +0 -269
  45. package/tauri-bundle-marker.test.mjs +0 -81
  46. package/upload-large.test.mjs +0 -70
@@ -1,103 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import { createHash } from "node:crypto";
3
- import { mkdtempSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
4
- import os from "node:os";
5
- import path from "node:path";
6
- import test from "node:test";
7
- import { execFileSync } from "node:child_process";
8
- import { canonicalAddonManifest, createAddonManifest } from "./addon-contract.mjs";
9
- import { prepareGitHubAddonRelease, prepareGitHubRelease, publishGitHubRelease } from "./github-release.mjs";
10
-
11
- const sha256 = (value) => createHash("sha256").update(value).digest("hex");
12
-
13
- function fixture() {
14
- const root = mkdtempSync(path.join(os.tmpdir(), "right-release-github-"));
15
- const releaseId = "cutright-1.2.3-abcdef12";
16
- const sealedDir = path.join(root, ".right-release", "sealed", releaseId, "windows");
17
- mkdirSync(sealedDir, { recursive: true });
18
- writeFileSync(path.join(sealedDir, "CutRight.exe"), "signed-installer");
19
- writeFileSync(path.join(sealedDir, "release-manifest.json"), `${JSON.stringify({
20
- schema: 1, releaseId, app: "cutright", version: "1.2.3", commit: "abcdef1234567890", platform: "win",
21
- files: [{ role: "installer", name: "CutRight.exe", sha256: sha256("signed-installer"), sizeBytes: 16 }],
22
- checkpoints: ["sealed"],
23
- })}\n`);
24
- return { root, releaseId };
25
- }
26
-
27
- function addonFixture() {
28
- const root = mkdtempSync(path.join(os.tmpdir(), "right-release-github-addon-"));
29
- mkdirSync(path.join(root, "out"));
30
- for (const [name, bytes] of [["membrane", "command"], ["membrane-service", "service"], ["icon.png", "icon"], ["LICENSE", "license"], ["EULA.txt", "eula"], ["PRIVACY.md", "privacy"], ["THIRD-PARTY-NOTICES.txt", "notices"]]) writeFileSync(path.join(root, "out", name), bytes);
31
- execFileSync("git", ["init", "--initial-branch", "main"], { cwd: root });
32
- execFileSync("git", ["config", "user.email", "test@example.com"], { cwd: root });
33
- execFileSync("git", ["config", "user.name", "Test"], { cwd: root });
34
- writeFileSync(path.join(root, "tracked"), "source"); execFileSync("git", ["add", "."], { cwd: root }); execFileSync("git", ["commit", "-m", "source"], { cwd: root });
35
- const files = [["command", "membrane", true], ["service", "membrane-service", true], ["icon", "icon.png", false], ["license", "LICENSE", false], ["eula", "EULA.txt", false], ["privacy", "PRIVACY.md", false], ["third-party-notices", "THIRD-PARTY-NOTICES.txt", false]].map(([role, name, executable]) => ({ role, name, source: `out/${name}`, executable }));
36
- const config = { schema: 1, kind: "headless-addon", addon: "membrane", version: "0.1.0", packageManager: "pnpm", distribution: { provider: "github-releases", repository: "Orthic-Labs/Membrane" }, checks: [], buildInputs: { include: ["tracked"] }, consumer: { contract: "membrane-product-v1" }, targets: { win: { targetTriple: "x86_64-pc-windows-msvc", build: { cmd: "false", args: [] }, signing: { contract: "azure-artifact-signing-v1" }, files } } };
37
- const commit = execFileSync("git", ["rev-parse", "HEAD"], { cwd: root, encoding: "utf8" }).trim();
38
- const manifest = createAddonManifest({ config, root, platform: "win", commit, signing: { command: { contract: "test-fixture-v1", status: "verified" }, service: { contract: "test-fixture-v1", status: "verified" } } });
39
- const sealed = path.join(root, ".right-release", "addons", "membrane", "0.1.0", commit.slice(0, 8), "win"); mkdirSync(sealed, { recursive: true });
40
- for (const file of manifest.files) writeFileSync(path.join(sealed, file.name), readFileSync(path.join(root, "out", file.name)));
41
- writeFileSync(path.join(sealed, "addon-manifest.json"), canonicalAddonManifest(manifest));
42
- return { root, config };
43
- }
44
-
45
- test("GitHub plan derives tag, notes, installer, manifest & checksums only from sealed bytes", () => {
46
- const fx = fixture();
47
- const plan = prepareGitHubRelease({ repoRoot: fx.root, releaseId: fx.releaseId, platform: "win", repo: "Orthic-Labs/CutRight" });
48
- assert.equal(plan.tag, "v1.2.3");
49
- assert.equal(plan.assets.length, 3);
50
- assert.match(plan.notes, /abcdef1234567890/);
51
- assert.equal(readFileSync(plan.assets[2], "utf8"), `${sha256("signed-installer")} CutRight.exe\n`);
52
- });
53
-
54
- test("dry-run checks public visibility without creating or uploading a release", () => {
55
- const fx = fixture();
56
- const plan = prepareGitHubRelease({ repoRoot: fx.root, releaseId: fx.releaseId, platform: "win", repo: "Orthic-Labs/CutRight" });
57
- const calls = [];
58
- const run = (_command, args, options = {}) => {
59
- calls.push(args);
60
- if (args[0] === "repo") return JSON.stringify({ visibility: "PUBLIC" });
61
- if (args[0] === "release" && args[1] === "view") return options.allowFailure ? { ok: false } : "";
62
- throw new Error(`unexpected mutation: ${args.join(" ")}`);
63
- };
64
- assert.equal(publishGitHubRelease(plan, { repo: "Orthic-Labs/CutRight", dryRun: true, run }).status, "would-create");
65
- assert.equal(calls.length, 2);
66
- });
67
-
68
- test("GitHub add-on plan uses a content-addressed platform tag and manifest URL", () => {
69
- const fx = addonFixture();
70
- const plan = prepareGitHubAddonRelease({ repoRoot: fx.root, config: fx.config, platform: "win", repo: "Orthic-Labs/Membrane" });
71
- assert.match(plan.tag, /^addon-membrane-v0\.1\.0-win-sha256-[0-9a-f]{64}$/);
72
- assert.equal(plan.assets.at(-1), plan.manifestPath);
73
- assert.equal(plan.manifestUrl, `https://github.com/Orthic-Labs/Membrane/releases/download/${plan.tag}/addon-manifest.json`);
74
- });
75
-
76
- test("existing GitHub add-on release is verified without upload mutation", () => {
77
- const asset = path.join(mkdtempSync(path.join(os.tmpdir(), "right-release-existing-")), "addon-manifest.json"); writeFileSync(asset, "sealed");
78
- const calls = [];
79
- const run = (_command, args, options = {}) => {
80
- calls.push(args);
81
- if (args[0] === "repo") return JSON.stringify({ visibility: "PUBLIC" });
82
- if (args[0] === "release" && args[1] === "view") return options.allowFailure ? { ok: true } : "";
83
- if (args[0] === "release" && args[1] === "download") { mkdirSync(args[args.indexOf("--dir") + 1], { recursive: true }); writeFileSync(path.join(args[args.indexOf("--dir") + 1], path.basename(asset)), "sealed"); return options.allowFailure ? { ok: true, output: "" } : ""; }
84
- throw new Error(`unexpected mutation: ${args.join(" ")}`);
85
- };
86
- const result = publishGitHubRelease({ kind: "addon", manifest: { files: [] }, sealedDir: path.dirname(asset), tag: "immutable", assets: [asset], manifestUrl: "https://example.test/manifest" }, { repo: "Orthic-Labs/Membrane", run });
87
- assert.equal(result.status, "already-verified");
88
- assert.equal(calls.some((args) => args[0] === "release" && args[1] === "upload"), false);
89
- });
90
-
91
- test("partial GitHub add-on release uploads only missing assets then verifies all", () => {
92
- const root = mkdtempSync(path.join(os.tmpdir(), "right-release-resume-")); const first = path.join(root, "first"); const second = path.join(root, "second"); writeFileSync(first, "one"); writeFileSync(second, "two");
93
- const remote = new Map([["first", "one"]]); const uploads = [];
94
- const run = (_command, args, options = {}) => {
95
- if (args[0] === "repo") return JSON.stringify({ visibility: "PUBLIC" });
96
- if (args[0] === "release" && args[1] === "view") return { ok: true };
97
- if (args[0] === "release" && args[1] === "download") { const name = args[args.indexOf("--pattern") + 1]; if (!remote.has(name)) return { ok: false, error: "no assets match" }; const dir = args[args.indexOf("--dir") + 1]; mkdirSync(dir, { recursive: true }); writeFileSync(path.join(dir, name), remote.get(name)); return { ok: true, output: "" }; }
98
- if (args[0] === "release" && args[1] === "upload") { for (const value of args.slice(3, args.indexOf("--repo"))) { uploads.push(path.basename(value)); remote.set(path.basename(value), readFileSync(value, "utf8")); } return ""; }
99
- throw new Error(`unexpected call: ${args.join(" ")}`);
100
- };
101
- const result = publishGitHubRelease({ kind: "addon", manifest: { files: [] }, sealedDir: root, tag: "immutable", assets: [first, second], manifestUrl: "https://example.test/manifest" }, { repo: "Orthic-Labs/Membrane", run });
102
- assert.equal(result.status, "resumed-verified"); assert.deepEqual(uploads, ["second"]);
103
- });
@@ -1,221 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
- import { mkdtempSync } from "node:fs";
4
- import os from "node:os";
5
- import path from "node:path";
6
- import test from "node:test";
7
-
8
- import {
9
- acquireHeavyWorkSlot,
10
- formatResourceSnapshot,
11
- heavyChildDetached,
12
- heavyCommandEnvironment,
13
- heavyWorkRoot,
14
- parseMacResourceSnapshot,
15
- parseWindowsResourceSnapshot,
16
- resourceBlockers,
17
- runHeavyCommand,
18
- sweepTrackedHeavyWorkOrphan,
19
- systemResourceSnapshot,
20
- terminateProcessTree,
21
- watchOwnedProcessTree,
22
- } from "./heavy-command.mjs";
23
-
24
- test("heavy-work root is machine-wide rather than repository-local", () => {
25
- assert.equal(heavyWorkRoot({ platform: "mac", home: "/Users/test", env: {} }), "/Users/test/Library/Caches/RightSuite/heavy-work");
26
- assert.equal(heavyWorkRoot({ platform: "win", env: { LOCALAPPDATA: "C:\\Users\\test\\AppData\\Local" } }), "C:\\Users\\test\\AppData\\Local\\RightSuite\\heavy-work");
27
- });
28
-
29
- test("heavy-work root ignores XDG_CACHE_HOME regardless of value", () => {
30
- assert.equal(
31
- heavyWorkRoot({ platform: "linux", env: { XDG_CACHE_HOME: "/one/path" }, home: "/home/u" }),
32
- heavyWorkRoot({ platform: "linux", env: {}, home: "/home/u" }),
33
- );
34
- assert.equal(heavyWorkRoot({ platform: "linux", env: {}, home: "/home/u" }), "/home/u/.cache/rightsuite/heavy-work");
35
- });
36
-
37
- test("heavy commands default compiler and test concurrency to two", () => {
38
- const env = heavyCommandEnvironment({});
39
- assert.equal(env.CARGO_BUILD_JOBS, "2");
40
- assert.equal(env.RUST_TEST_THREADS, "2");
41
- assert.equal(env.RIGHTSUITE_HEAVY_WORK_OWNER, "1");
42
- assert.equal(heavyCommandEnvironment({ CARGO_BUILD_JOBS: "3", RUST_TEST_THREADS: "1" }).CARGO_BUILD_JOBS, "3");
43
- });
44
-
45
- test("only the slot owner creates a new POSIX process group", () => {
46
- assert.equal(heavyChildDetached({ slot: {}, platform: "mac" }), true);
47
- assert.equal(heavyChildDetached({ slot: null, platform: "mac" }), false);
48
- assert.equal(heavyChildDetached({ slot: {}, platform: "win" }), false);
49
- });
50
-
51
- test("mac resource parser reports memory, swap and thermal limiting", () => {
52
- const snapshot = parseMacResourceSnapshot({
53
- memory: "System-wide memory free percentage: 9%",
54
- swap: "vm.swapusage: total = 8192.00M used = 7750.62M free = 441.38M",
55
- thermal: "CPU_Speed_Limit = 70",
56
- });
57
- assert.equal(snapshot.freePercent, 9);
58
- assert.equal(Math.round(snapshot.swapUsedBytes / 1024 ** 2), 7751);
59
- assert.equal(snapshot.thermalLimited, true);
60
- assert.deepEqual(resourceBlockers(snapshot), ["memory free 9% < 15%", "macOS reports thermal/performance limiting"]);
61
- });
62
-
63
- test("Windows admission uses physical memory and CPU load with a Node memory fallback", () => {
64
- const snapshot = parseWindowsResourceSnapshot({
65
- system: JSON.stringify({ TotalBytes: 16 * 1024 ** 3, FreeBytes: 4 * 1024 ** 3, CpuLoadPercent: 94 }),
66
- });
67
- assert.equal(snapshot.freePercent, 25);
68
- assert.equal(snapshot.cpuLoadPercent, 94);
69
- assert.deepEqual(resourceBlockers(snapshot), ["CPU load 94% > 90%"]);
70
-
71
- const fallback = parseWindowsResourceSnapshot({ system: "not-json", fallbackTotalBytes: 8 * 1024 ** 3, fallbackFreeBytes: 2 * 1024 ** 3 });
72
- assert.equal(fallback.freePercent, 25);
73
- assert.equal(fallback.cpuLoadPercent, null);
74
- });
75
-
76
- test("Windows resource snapshot queries PowerShell without requiring optional modules", () => {
77
- const calls = [];
78
- const snapshot = systemResourceSnapshot({
79
- platform: "win",
80
- run: (command, args) => { calls.push({ command, args }); return { status: 0, stdout: '{"TotalBytes":8589934592,"FreeBytes":1073741824,"CpuLoadPercent":42}' }; },
81
- totalmem: () => 1,
82
- freemem: () => 1,
83
- });
84
- assert.equal(calls[0].command, "powershell.exe");
85
- assert.deepEqual(calls[0].args.slice(0, 3), ["-NoLogo", "-NoProfile", "-NonInteractive"]);
86
- assert.equal(snapshot.freePercent, 12.5);
87
- assert.equal(snapshot.cpuLoadPercent, 42);
88
- assert.equal(snapshot.thermalLimited, false);
89
- assert.equal(formatResourceSnapshot(snapshot), "memory-free=12.5% cpu-load=42%");
90
- });
91
-
92
- test("nested heavy command preserves ownership without another slot", async () => {
93
- const code = await runHeavyCommand(["--", process.execPath, "-e", "process.exit(0)"], { env: { ...process.env, RIGHTSUITE_HEAVY_WORK_OWNER: "1" } });
94
- assert.equal(code, 0);
95
- });
96
-
97
- test("owned process-tree termination targets a POSIX group or Windows task tree", () => {
98
- const signals = [];
99
- terminateProcessTree(202, { platform: "mac", kill: (pid, signal) => signals.push([pid, signal]), treeAlive: () => true, pause: () => {} });
100
- assert.deepEqual(signals, [[-202, "SIGTERM"], [-202, "SIGKILL"]]);
101
- const calls = [];
102
- terminateProcessTree(303, { platform: "win", run: (command, args) => calls.push([command, args]) });
103
- assert.deepEqual(calls, [["taskkill", ["/PID", "303", "/T", "/F"]]]);
104
- });
105
-
106
- test("slot serializes contenders and removes stale owners", () => {
107
- const root = mkdtempSync(path.join(os.tmpdir(), "rightsuite-heavy-"));
108
- const quiet = () => {};
109
- const snapshot = () => ({ freePercent: 50, swapUsedBytes: 0, swapTotalBytes: 0, thermalLimited: false });
110
- const first = acquireHeavyWorkSlot({ root, pid: 101, alive: (pid) => pid === 101, snapshot, log: quiet });
111
- assert.throws(
112
- () => acquireHeavyWorkSlot({ root, pid: 202, waitMs: 1, pollMs: 1, alive: (pid) => pid === 101, pause: () => {}, snapshot, log: quiet }),
113
- /holder pid 101/,
114
- );
115
- first.release();
116
-
117
- const lockDir = path.join(root, "slot");
118
- mkdirSync(lockDir);
119
- writeFileSync(path.join(lockDir, "owner.json"), JSON.stringify({ pid: 303, token: "stale" }));
120
- const replacement = acquireHeavyWorkSlot({ root, pid: 404, alive: () => false, snapshot, log: quiet });
121
- replacement.release();
122
- });
123
-
124
- test("dead slot owner reaps its recorded matching child tree", () => {
125
- const root = mkdtempSync(path.join(os.tmpdir(), "rightsuite-heavy-owned-"));
126
- const killed = [];
127
- const options = {
128
- root, snapshot: () => ({ freePercent: 50, thermalLimited: false }), log: () => {},
129
- alive: (pid) => pid === 202,
130
- processStartedAt: (pid) => pid === 202 ? 1234 : null,
131
- terminate: (pid) => killed.push(pid),
132
- startWatcher: () => {},
133
- };
134
- const first = acquireHeavyWorkSlot({ ...options, pid: 101 });
135
- first.trackChild(202, "cargo");
136
- assert.equal(JSON.parse(readFileSync(path.join(root, "slot", "child.json"), "utf8")).childPid, 202);
137
- const replacement = acquireHeavyWorkSlot({ ...options, pid: 404 });
138
- assert.deepEqual(killed, [202]);
139
- replacement.release();
140
- });
141
-
142
- test("dead slot owner refuses to kill a reused child PID", () => {
143
- const root = mkdtempSync(path.join(os.tmpdir(), "rightsuite-heavy-reused-"));
144
- let observedStart = 1234;
145
- const options = {
146
- root, snapshot: () => ({ freePercent: 50, thermalLimited: false }), log: () => {},
147
- alive: (pid) => pid === 202,
148
- processStartedAt: (pid) => pid === 202 ? observedStart : null,
149
- terminate: () => assert.fail("reused PID must not be terminated"),
150
- startWatcher: () => {},
151
- };
152
- const first = acquireHeavyWorkSlot({ ...options, pid: 101 });
153
- first.trackChild(202, "cargo");
154
- observedStart = 5678;
155
- assert.throws(() => acquireHeavyWorkSlot({ ...options, pid: 404 }), /identity changed/);
156
- first.release();
157
- });
158
-
159
- test("preflight sweep reaps only a tracked child after an owner PID is reused", () => {
160
- const root = mkdtempSync(path.join(os.tmpdir(), "rightsuite-heavy-preflight-"));
161
- const lockDir = path.join(root, "slot");
162
- mkdirSync(lockDir);
163
- writeFileSync(path.join(lockDir, "owner.json"), JSON.stringify({ pid: 101, ownerStartedAtMs: 100, token: "stale" }));
164
- writeFileSync(path.join(lockDir, "child.json"), JSON.stringify({ childPid: 202, childStartedAtMs: 200 }));
165
- const killed = [];
166
- assert.equal(sweepTrackedHeavyWorkOrphan({
167
- root,
168
- alive: (pid) => pid === 101 || pid === 202,
169
- startedAt: (pid) => pid === 101 ? 5_000 : pid === 202 ? 200 : null,
170
- terminate: (pid) => killed.push(pid),
171
- log: () => {},
172
- }), true);
173
- assert.deepEqual(killed, [202]);
174
- assert.equal(existsSync(lockDir), false);
175
- });
176
-
177
- test("preflight sweep refuses a reused tracked child PID instead of killing broadly", () => {
178
- const root = mkdtempSync(path.join(os.tmpdir(), "rightsuite-heavy-preflight-reused-"));
179
- const lockDir = path.join(root, "slot");
180
- mkdirSync(lockDir);
181
- writeFileSync(path.join(lockDir, "owner.json"), JSON.stringify({ pid: 101, ownerStartedAtMs: 100, token: "stale" }));
182
- writeFileSync(path.join(lockDir, "child.json"), JSON.stringify({ childPid: 202, childStartedAtMs: 200 }));
183
- assert.throws(() => sweepTrackedHeavyWorkOrphan({
184
- root,
185
- alive: (pid) => pid === 101 || pid === 202,
186
- startedAt: (pid) => pid === 101 ? 5_000 : pid === 202 ? 4_000 : null,
187
- terminate: () => assert.fail("reused child PID must not be terminated"),
188
- log: () => {},
189
- }), /identity changed/);
190
- });
191
-
192
- test("preflight sweep refuses a tracked child without a verifiable start identity", () => {
193
- const root = mkdtempSync(path.join(os.tmpdir(), "rightsuite-heavy-preflight-unverified-"));
194
- const lockDir = path.join(root, "slot");
195
- mkdirSync(lockDir);
196
- writeFileSync(path.join(lockDir, "owner.json"), JSON.stringify({ pid: 101, ownerStartedAtMs: 100, token: "stale" }));
197
- writeFileSync(path.join(lockDir, "child.json"), JSON.stringify({ childPid: 202, childStartedAtMs: 200 }));
198
- assert.throws(() => sweepTrackedHeavyWorkOrphan({
199
- root,
200
- alive: (pid) => pid === 101 || pid === 202,
201
- startedAt: (pid) => pid === 101 ? 5_000 : null,
202
- terminate: () => assert.fail("unverified child PID must not be terminated"),
203
- log: () => {},
204
- }), /identity changed/);
205
- });
206
-
207
- test("detached watcher reaps an owned child as soon as its owner disappears", async () => {
208
- const root = mkdtempSync(path.join(os.tmpdir(), "rightsuite-heavy-watch-"));
209
- const lockDir = path.join(root, "slot");
210
- mkdirSync(lockDir);
211
- writeFileSync(path.join(lockDir, "owner.json"), JSON.stringify({ pid: 101, token: "owned" }));
212
- writeFileSync(path.join(lockDir, "child.json"), JSON.stringify({ childPid: 202, childStartedAtMs: 1234 }));
213
- const killed = [];
214
- const result = await watchOwnedProcessTree(
215
- { root, token: "owned", ownerPid: 101, childPid: 202, childStartedAtMs: 1234 },
216
- { alive: (pid) => pid === 202, startedAt: () => 1234, terminate: (pid) => killed.push(pid), pause: async () => {} },
217
- );
218
- assert.equal(result, true);
219
- assert.deepEqual(killed, [202]);
220
- assert.equal(existsSync(lockDir), false);
221
- });
@@ -1,151 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import { createHash } from "node:crypto";
3
- import { mkdirSync, mkdtempSync, readFileSync, writeFileSync } from "node:fs";
4
- import os from "node:os";
5
- import path from "node:path";
6
- import test from "node:test";
7
- import { assertLegalReleaseContract } from "./legal-contract.mjs";
8
-
9
- function digest(text) {
10
- return createHash("sha256").update(text).digest("hex");
11
- }
12
-
13
- function fixture() {
14
- const root = mkdtempSync(path.join(os.tmpdir(), "right-legal-contract-"));
15
- const legalDir = path.join(root, "legal");
16
- const tauriDir = path.join(root, "src-tauri");
17
- mkdirSync(legalDir, { recursive: true });
18
- mkdirSync(tauriDir, { recursive: true });
19
- const definitions = [
20
- ["license", "LICENSE.md", "Ownership notice\n"],
21
- ["eula", "EULA.md", "End user license agreement\n"],
22
- ["acceptable_use", "ACCEPTABLE_USE.md", "Acceptable use\n"],
23
- ["product_schedule", "PRODUCT_SCHEDULE.md", "Product schedule\n"],
24
- ["privacy_notice", "PRIVACY.md", "Privacy notice\n"],
25
- ["third_party_notices", "THIRD_PARTY_NOTICES.md", "Build provenance: fixture 1.0 windows-x86_64; inputs: pnpm-lock=abc Cargo.lock=def; generated-by=fixture\n"],
26
- ];
27
- for (const [, file, content] of definitions) writeFileSync(path.join(legalDir, file), content);
28
- const manifest = {
29
- schema: 2,
30
- suite: "right-suite-desktop",
31
- appKey: "fixture",
32
- productName: "Fixture",
33
- licensor: "Damned Ventures LLC",
34
- effectiveDate: "2026-07-16",
35
- acceptanceVersion: "fixture-2026-07-16-v1",
36
- documents: definitions.map(([role, file, content]) => ({
37
- id: `fixture-${role}`,
38
- role,
39
- title: role,
40
- version: "1.0",
41
- sha256: digest(content),
42
- path: file,
43
- publicUrl: `https://example.test/legal/${role}`,
44
- treatment: ["privacy_notice"].includes(role) ? "acknowledge" : role === "third_party_notices" ? "notice" : "agree",
45
- material: !["privacy_notice", "third_party_notices"].includes(role),
46
- })),
47
- };
48
- writeFileSync(path.join(legalDir, "legal-manifest.json"), `${JSON.stringify(manifest, null, 2)}\n`);
49
- // No bundle.licenseFile: the in-app gate is the single assent surface, so a
50
- // compliant app ships an installer with no license page (Adrian, 2026-07-17).
51
- writeFileSync(
52
- path.join(tauriDir, "tauri.conf.json"),
53
- `${JSON.stringify({ bundle: { publisher: "Damned Ventures LLC" } }, null, 2)}\n`,
54
- );
55
- return {
56
- root,
57
- legal: { manifest: "legal/legal-manifest.json", tauriConfig: "src-tauri/tauri.conf.json" },
58
- manifest,
59
- };
60
- }
61
-
62
- test("accepts a complete, hash-bound legal snapshot", () => {
63
- const { root, legal } = fixture();
64
- const result = assertLegalReleaseContract(root, legal, "fixture", "win");
65
- assert.equal(result.appKey, "fixture");
66
- assert.equal(result.documents.length, 6);
67
- });
68
-
69
- test("rejects a missing legal config", () => {
70
- const { root } = fixture();
71
- assert.throws(() => assertLegalReleaseContract(root, null, "fixture", "win"), /must declare legal\.manifest/i);
72
- });
73
-
74
- test("rejects changed bytes and unsafe manifest paths", () => {
75
- const changed = fixture();
76
- writeFileSync(path.join(changed.root, "legal", "EULA.md"), "changed without a new manifest\n");
77
- assert.throws(() => assertLegalReleaseContract(changed.root, changed.legal, "fixture", "win"), /EULA\.md.*sha-?256/i);
78
-
79
- const traversal = fixture();
80
- traversal.manifest.documents[0].path = "../outside.md";
81
- writeFileSync(path.join(traversal.root, "legal", "legal-manifest.json"), `${JSON.stringify(traversal.manifest)}\n`);
82
- assert.throws(() => assertLegalReleaseContract(traversal.root, traversal.legal, "fixture", "win"), /path.*snapshot|traversal/i);
83
- });
84
-
85
- test("rejects placeholders and unresolved checklist markers", () => {
86
- const { root, legal, manifest } = fixture();
87
- const eula = "Terms [COUNSEL: decide later]\n- [ ] fill this in\n";
88
- writeFileSync(path.join(root, "legal", "EULA.md"), eula);
89
- manifest.documents.find((document) => document.role === "eula").sha256 = digest(eula);
90
- writeFileSync(path.join(root, "legal", "legal-manifest.json"), `${JSON.stringify(manifest)}\n`);
91
- assert.throws(() => assertLegalReleaseContract(root, legal, "fixture", "win"), /placeholder|unchecked|release blocker/i);
92
- });
93
-
94
- test("rejects an explicitly unresolved release blocker even without a checkbox", () => {
95
- const { root, legal, manifest } = fixture();
96
- const notice = "Build provenance: fixture; inputs: lock=abc; generated-by=fixture\nRELEASE BLOCKER: exact model notice is unresolved.\n";
97
- writeFileSync(path.join(root, "legal", "THIRD_PARTY_NOTICES.md"), notice);
98
- manifest.documents.find((document) => document.role === "third_party_notices").sha256 = digest(notice);
99
- writeFileSync(path.join(root, "legal", "legal-manifest.json"), `${JSON.stringify(manifest)}\n`);
100
- assert.throws(() => assertLegalReleaseContract(root, legal, "fixture", "win"), /release blocker/i);
101
- });
102
-
103
- test("rejects a Windows installer license page: the in-app gate is the only assent surface", () => {
104
- const { root, legal } = fixture();
105
- writeFileSync(path.join(root, "src-tauri", "tauri.conf.json"), JSON.stringify({ bundle: { licenseFile: "../legal/EULA.md" } }));
106
- assert.throws(() => assertLegalReleaseContract(root, legal, "fixture", "win"), /licenseFile must be absent/i);
107
- });
108
-
109
- test("rejects a macOS DMG SLA too — one licenseFile drives both bundlers", () => {
110
- // branded-dmg.mjs derives its --eula from bundle.licenseFile, so a Windows-only
111
- // check would let a mac release quietly reintroduce the mount-time SLA.
112
- const { root, legal } = fixture();
113
- writeFileSync(path.join(root, "src-tauri", "tauri.conf.json"), JSON.stringify({ bundle: { licenseFile: "../legal/EULA.md" } }));
114
- assert.throws(() => assertLegalReleaseContract(root, legal, "fixture", "mac"), /licenseFile must be absent/i);
115
- });
116
-
117
- test("accepts an app whose installer carries no license page", () => {
118
- const { root, legal } = fixture();
119
- for (const platform of ["win", "mac"]) {
120
- assert.doesNotThrow(() => assertLegalReleaseContract(root, legal, "fixture", platform));
121
- }
122
- });
123
-
124
- test("requires third-party build provenance", () => {
125
- const { root, legal, manifest } = fixture();
126
- const notice = "Third-party software is included.\n";
127
- writeFileSync(path.join(root, "legal", "THIRD_PARTY_NOTICES.md"), notice);
128
- manifest.documents.find((document) => document.role === "third_party_notices").sha256 = digest(notice);
129
- writeFileSync(path.join(root, "legal", "legal-manifest.json"), `${JSON.stringify(manifest)}\n`);
130
- assert.throws(() => assertLegalReleaseContract(root, legal, "fixture", "win"), /third-party.*provenance/i);
131
- });
132
-
133
- test("manifest hash evidence is the hash of the manifest bytes", () => {
134
- const { root, legal } = fixture();
135
- const result = assertLegalReleaseContract(root, legal, "fixture", "mac");
136
- const bytes = readFileSync(path.join(root, legal.manifest));
137
- assert.equal(result.manifestSha256, createHash("sha256").update(bytes).digest("hex"));
138
- });
139
-
140
- test("hash-binds declared third-party appendices and rejects missing files", () => {
141
- const { root, legal, manifest } = fixture();
142
- const appendix = "full dependency license text\n";
143
- writeFileSync(path.join(root, "legal", "RUST_LICENSES.txt"), appendix);
144
- manifest.appendices = [{ id: "rust-licenses", title: "Rust licenses", path: "RUST_LICENSES.txt", sha256: digest(appendix) }];
145
- writeFileSync(path.join(root, "legal", "legal-manifest.json"), `${JSON.stringify(manifest)}\n`);
146
- assert.equal(assertLegalReleaseContract(root, legal, "fixture", "mac").appendices.length, 1);
147
-
148
- manifest.appendices[0].path = "missing.txt";
149
- writeFileSync(path.join(root, "legal", "legal-manifest.json"), `${JSON.stringify(manifest)}\n`);
150
- assert.throws(() => assertLegalReleaseContract(root, legal, "fixture", "mac"), /appendix.*missing/i);
151
- });
@@ -1,58 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import { execFileSync } from "node:child_process";
3
- import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
4
- import os from "node:os";
5
- import path from "node:path";
6
- import test from "node:test";
7
- import { fileURLToPath } from "node:url";
8
-
9
- const helper = fileURLToPath(new URL("./mirror-root-artifact.mjs", import.meta.url));
10
-
11
- test("mirrors a nested package artifact from a linked worktree into both package roots", () => {
12
- const temp = mkdtempSync(path.join(os.tmpdir(), "right-release-mirror-"));
13
- const main = path.join(temp, "suite");
14
- const worktree = path.join(temp, "release-worktree");
15
- try {
16
- mkdirSync(path.join(main, "apps", "desktop"), { recursive: true });
17
- git(temp, "init", "suite");
18
- git(main, "config", "user.email", "test@example.com");
19
- git(main, "config", "user.name", "Right Release Test");
20
- writeFileSync(path.join(main, "tracked"), "fixture\n");
21
- git(main, "add", "tracked");
22
- git(main, "commit", "-m", "fixture");
23
- git(main, "worktree", "add", worktree, "-b", "release-test");
24
-
25
- const packageRoot = path.join(worktree, "apps", "desktop");
26
- const source = path.join(packageRoot, "target", "App_1.0.0.dmg");
27
- mkdirSync(path.dirname(source), { recursive: true });
28
- writeFileSync(source, "signed-dmg-fixture");
29
-
30
- execFileSync(process.execPath, [helper, "--file", source, "--package-root", packageRoot], { encoding: "utf8" });
31
-
32
- assert.equal(readFileSync(path.join(packageRoot, "App_1.0.0.dmg"), "utf8"), "signed-dmg-fixture");
33
- assert.equal(readFileSync(path.join(main, "apps", "desktop", "App_1.0.0.dmg"), "utf8"), "signed-dmg-fixture");
34
- } finally {
35
- rmSync(temp, { recursive: true, force: true });
36
- }
37
- });
38
-
39
- test("copies a main-worktree artifact into its package root", () => {
40
- const temp = mkdtempSync(path.join(os.tmpdir(), "right-release-mirror-main-"));
41
- try {
42
- git(temp, "init", "app");
43
- const root = path.join(temp, "app");
44
- const source = path.join(root, "target", "App_2.0.0.dmg");
45
- mkdirSync(path.dirname(source), { recursive: true });
46
- writeFileSync(source, "main-worktree-dmg");
47
-
48
- execFileSync(process.execPath, [helper, "--file", source, "--package-root", root], { encoding: "utf8" });
49
-
50
- assert.equal(readFileSync(path.join(root, "App_2.0.0.dmg"), "utf8"), "main-worktree-dmg");
51
- } finally {
52
- rmSync(temp, { recursive: true, force: true });
53
- }
54
- });
55
-
56
- function git(cwd, ...args) {
57
- return execFileSync("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
58
- }