@rightkit/release 0.2.18 → 0.2.20

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.
@@ -0,0 +1,159 @@
1
+ import { existsSync, mkdtempSync, readdirSync, rmSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import path from "node:path";
4
+ import { spawnSync } from "node:child_process";
5
+
6
+ const SKIP_DIRECTORIES = new Set([".cache", ".git", ".claude", ".worktrees", "node_modules", "target", "vendor"]);
7
+ const CRATES_IO_SOURCES = new Set([
8
+ "registry+https://github.com/rust-lang/crates.io-index",
9
+ "registry+https://index.crates.io/",
10
+ ]);
11
+
12
+ export function validateRightKitCargoContract(root, publishedVersions, label = path.basename(root)) {
13
+ const scanRoot = path.resolve(root);
14
+ const boundary = findRepositoryRoot(scanRoot);
15
+ const published = publishedVersions instanceof Map
16
+ ? publishedVersions
17
+ : new Map(Object.entries(publishedVersions ?? {}));
18
+ const manifests = findCargoManifests(scanRoot);
19
+ const cargoHome = mkdtempSync(path.join(tmpdir(), "rightkit-cargo-home-"));
20
+ try {
21
+ for (const manifestPath of manifests) {
22
+ const manifestLabel = path.relative(boundary, manifestPath) || "Cargo.toml";
23
+ assertNoRightKitCargoOverrides(manifestPath, boundary, `${label}/${manifestLabel}`);
24
+ const dependencies = readCargoManifestDependencies(manifestPath, cargoHome, `${label}/${manifestLabel}`);
25
+ assertPublishedRightKitCargoDependencies(dependencies, published, `${label}/${manifestLabel}`);
26
+ }
27
+ } finally {
28
+ rmSync(cargoHome, { recursive: true, force: true });
29
+ }
30
+ return manifests.length;
31
+ }
32
+
33
+ export function assertPublishedRightKitCargoDependencies(dependencies, published, label) {
34
+ let checked = 0;
35
+ for (const dependency of dependencies.filter(({ name }) => name.startsWith("rightkit-"))) {
36
+ checked += 1;
37
+ const expected = published.get(dependency.name);
38
+ if (!expected) throw new Error(`${label} ${dependency.name} is not a published RightKit crate`);
39
+ if (!CRATES_IO_SOURCES.has(dependency.source) || dependency.path || dependency.registry || dependency.req !== `=${expected}`) {
40
+ throw new Error(`${label} ${dependency.name} must use the exact crates.io version "=${expected}"`);
41
+ }
42
+ }
43
+ return checked;
44
+ }
45
+
46
+ export function assertNoRightKitCargoOverrides(manifestPath, repoRoot, label) {
47
+ const boundary = path.resolve(repoRoot);
48
+ const manifest = path.resolve(manifestPath);
49
+ assertInsideBoundary(manifest, boundary);
50
+ assertNoRightKitOverrides(readToml(manifest, label), label, path.relative(boundary, manifest) || "Cargo.toml");
51
+
52
+ for (const configPath of findRepoCargoConfigs(path.dirname(manifest), boundary)) {
53
+ const source = path.relative(boundary, configPath);
54
+ const parsed = readToml(configPath, source);
55
+ assertNoRightKitOverrides(parsed, label, source);
56
+ const cratesIo = parsed.source?.["crates-io"];
57
+ if (cratesIo && typeof cratesIo === "object"
58
+ && ["replace-with", "directory", "git", "registry", "local-registry"].some((key) => key in cratesIo)) {
59
+ throw new Error(`${label} has forbidden crates.io source replacement in ${source}`);
60
+ }
61
+ }
62
+ }
63
+
64
+ function readCargoManifestDependencies(manifestPath, cargoHome, label) {
65
+ const result = spawnSync(
66
+ "cargo",
67
+ ["metadata", "--no-deps", "--offline", "--format-version", "1", "--manifest-path", manifestPath],
68
+ {
69
+ cwd: path.dirname(manifestPath),
70
+ encoding: "utf8",
71
+ env: { ...process.env, CARGO_HOME: cargoHome },
72
+ windowsHide: true,
73
+ },
74
+ );
75
+ if (result.status !== 0) {
76
+ throw new Error(`${label} Cargo metadata rejected manifest; RightKit dependencies must use exact crates.io versions: ${String(result.stderr ?? "").trim()}`);
77
+ }
78
+ const metadata = JSON.parse(result.stdout);
79
+ const expectedPath = path.resolve(manifestPath).toLowerCase();
80
+ const pkg = metadata.packages.find(({ manifest_path: candidate }) => path.resolve(candidate).toLowerCase() === expectedPath);
81
+ if (!pkg && path.resolve(metadata.workspace_root, "Cargo.toml").toLowerCase() === expectedPath) return [];
82
+ if (!pkg) throw new Error(`${label} was not returned by Cargo metadata`);
83
+ return pkg.dependencies;
84
+ }
85
+
86
+ function assertNoRightKitOverrides(parsed, label, source) {
87
+ for (const overrides of Object.values(parsed.patch ?? {})) {
88
+ if (!overrides || typeof overrides !== "object" || Array.isArray(overrides)) continue;
89
+ for (const [name, specifier] of Object.entries(overrides)) {
90
+ const packageName = typeof specifier === "object" && specifier ? specifier.package : undefined;
91
+ if (name.startsWith("rightkit-") || packageName?.startsWith("rightkit-")) {
92
+ throw new Error(`${label} has forbidden RightKit Cargo override ${name} in ${source}`);
93
+ }
94
+ }
95
+ }
96
+ for (const [name, specifier] of Object.entries(parsed.replace ?? {})) {
97
+ const packageName = typeof specifier === "object" && specifier ? specifier.package : undefined;
98
+ if (name.split(":", 1)[0].startsWith("rightkit-") || packageName?.startsWith("rightkit-")) {
99
+ throw new Error(`${label} has forbidden RightKit Cargo override ${name} in ${source}`);
100
+ }
101
+ }
102
+ }
103
+
104
+ function readToml(file, label) {
105
+ const script = "import json,pathlib,sys,tomllib; json.dump(tomllib.loads(pathlib.Path(sys.argv[1]).read_text(encoding='utf-8')),sys.stdout,default=str)";
106
+ const command = process.platform === "win32" ? "py" : "python3";
107
+ const args = process.platform === "win32" ? ["-3.11", "-c", script, file] : ["-c", script, file];
108
+ const result = spawnSync(command, args, { encoding: "utf8", windowsHide: true });
109
+ if (result.status !== 0) throw new Error(`${label} is not valid TOML: ${String(result.stderr ?? "").trim()}`);
110
+ return JSON.parse(result.stdout);
111
+ }
112
+
113
+ function findCargoManifests(root) {
114
+ const found = [];
115
+ const visit = (dir) => {
116
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
117
+ if (entry.isDirectory() && SKIP_DIRECTORIES.has(entry.name)) continue;
118
+ const full = path.join(dir, entry.name);
119
+ if (entry.isDirectory()) visit(full);
120
+ else if (entry.isFile() && entry.name === "Cargo.toml") found.push(full);
121
+ }
122
+ };
123
+ visit(root);
124
+ return found;
125
+ }
126
+
127
+ function findRepoCargoConfigs(start, repoRoot) {
128
+ const boundary = path.resolve(repoRoot);
129
+ let current = path.resolve(start);
130
+ assertInsideBoundary(current, boundary);
131
+ const configs = [];
132
+ while (true) {
133
+ for (const filename of ["config", "config.toml"]) {
134
+ const candidate = path.join(current, ".cargo", filename);
135
+ if (existsSync(candidate)) configs.push(candidate);
136
+ }
137
+ if (current.toLowerCase() === boundary.toLowerCase()) break;
138
+ current = path.dirname(current);
139
+ }
140
+ return configs;
141
+ }
142
+
143
+ function assertInsideBoundary(candidate, boundary) {
144
+ const relative = path.relative(boundary, candidate);
145
+ if (relative !== "" && (relative.startsWith("..") || path.isAbsolute(relative))) {
146
+ throw new Error(`${candidate} is outside ${boundary}`);
147
+ }
148
+ }
149
+
150
+ function findRepositoryRoot(start) {
151
+ const original = path.resolve(start);
152
+ let current = original;
153
+ while (true) {
154
+ if (existsSync(path.join(current, ".git"))) return current;
155
+ const parent = path.dirname(current);
156
+ if (parent === current) return original;
157
+ current = parent;
158
+ }
159
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rightkit/release",
3
- "version": "0.2.18",
3
+ "version": "0.2.20",
4
4
  "description": "Portable Right Suite release CLI/SDK: signed installers, updater artifacts, patch/update routing, hardening, R2 publish, and RightApps registration.",
5
5
  "type": "module",
6
6
  "bin": {
package/release.mjs CHANGED
@@ -4,6 +4,7 @@ import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "
4
4
  import path from "node:path";
5
5
  import { fileURLToPath, pathToFileURL } from "node:url";
6
6
  import { spawn, spawnSync } from "node:child_process";
7
+ import { validateRightKitCargoContract } from "./cargo-contract.mjs";
7
8
 
8
9
  const TOOL_ROOT = path.dirname(fileURLToPath(import.meta.url));
9
10
  const HARDENING_SCAN = path.resolve(TOOL_ROOT, "hardeningscan.mjs");
@@ -62,6 +63,7 @@ if (config.schema !== 1) fail(`unsupported config schema: ${config.schema ?? "<m
62
63
  const root = path.dirname(configPath);
63
64
  const workdir = path.resolve(root, config.workdir ?? ".");
64
65
  await validateRightKitPackageContract(root, config.app);
66
+ validateRightKitCargoContract(root, RIGHTKIT_VERSIONS.cargo, config.app ?? path.basename(root));
65
67
  const target = config.targets?.[opts.platform];
66
68
  if (!target) fail(`${config.app ?? "app"} has no ${opts.platform} release target`);
67
69
  if (target.signed !== true) {
package/release.test.mjs CHANGED
@@ -9,8 +9,10 @@ import test from "node:test";
9
9
  const release = fileURLToPath(new URL("./release.mjs", import.meta.url));
10
10
  const versions = JSON.parse(readFileSync(new URL("./rightkit-versions.json", import.meta.url), "utf8"));
11
11
 
12
- function fixture({ signed = true, publish = false, packageJson } = {}) {
13
- const dir = mkdtempSync(path.join(os.tmpdir(), "right-release-test-"));
12
+ function fixture({ signed = true, publish = false, packageJson, cargoManifest, cargoConfig, repoCargoConfig, siblingCargoManifest } = {}) {
13
+ const fixtureRoot = mkdtempSync(path.join(os.tmpdir(), "right-release-test-"));
14
+ const dir = repoCargoConfig ? path.join(fixtureRoot, "apps", "fixture") : fixtureRoot;
15
+ mkdirSync(dir, { recursive: true });
14
16
  const config = path.join(dir, "right-release.config.mjs");
15
17
  writeFileSync(
16
18
  path.join(dir, "package.json"),
@@ -49,6 +51,26 @@ function fixture({ signed = true, publish = false, packageJson } = {}) {
49
51
  },
50
52
  })};\n`,
51
53
  );
54
+ if (cargoManifest) {
55
+ writeFileSync(path.join(dir, "Cargo.toml"), `${cargoManifest}\n[lib]\npath = "fixture.rs"\n`, "utf8");
56
+ writeFileSync(path.join(dir, "fixture.rs"), "", "utf8");
57
+ }
58
+ if (cargoConfig) {
59
+ const cargoDir = path.join(dir, ".cargo");
60
+ mkdirSync(cargoDir, { recursive: true });
61
+ writeFileSync(path.join(cargoDir, "config.toml"), cargoConfig, "utf8");
62
+ }
63
+ if (repoCargoConfig) {
64
+ mkdirSync(path.join(fixtureRoot, ".git"), { recursive: true });
65
+ const cargoDir = path.join(fixtureRoot, ".cargo");
66
+ mkdirSync(cargoDir, { recursive: true });
67
+ writeFileSync(path.join(cargoDir, "config.toml"), repoCargoConfig, "utf8");
68
+ }
69
+ if (siblingCargoManifest) {
70
+ const sibling = path.join(fixtureRoot, "docs", "archived-workspace");
71
+ mkdirSync(sibling, { recursive: true });
72
+ writeFileSync(path.join(sibling, "Cargo.toml"), siblingCargoManifest, "utf8");
73
+ }
52
74
  return config;
53
75
  }
54
76
 
@@ -171,6 +193,46 @@ test("rejects hosted workflow files before release work starts", () => {
171
193
  assert.match(result.stderr, /hosted workflow.*forbidden/i);
172
194
  });
173
195
 
196
+ test("doctor accepts an exact crates.io RightKit pin with benign Cargo config", () => {
197
+ const config = fixture({
198
+ cargoManifest: `[package]\nname = "fixture"\nversion = "0.0.0"\nedition = "2021"\n[dependencies]\nrightkit-license = "=0.1.2"`,
199
+ cargoConfig: `[net]\nretry = 2\n[registries.private]\nindex = "https://example.test/index"`,
200
+ });
201
+ const result = run(config, "--doctor");
202
+ assert.equal(result.status, 0, result.stderr);
203
+ assert.match(result.stdout, /right-release/);
204
+ });
205
+
206
+ test("doctor scopes Cargo inspection to the configured app inside a monorepo", () => {
207
+ const config = fixture({
208
+ cargoManifest: `[package]\nname = "fixture"\nversion = "0.0.0"\nedition = "2021"\n[dependencies]\nrightkit-license = "=0.1.2"`,
209
+ repoCargoConfig: `[net]\nretry = 2`,
210
+ siblingCargoManifest: `[package]\nname = "archived-copy"\nversion = "0.0.0"\n[dependencies]\nrightkit-license = { path = "../../rightkit-license" }`,
211
+ });
212
+ const result = run(config, "--doctor");
213
+ assert.equal(result.status, 0, result.stderr);
214
+ });
215
+
216
+ test("doctor rejects a repo-local RightKit Cargo override", () => {
217
+ const config = fixture({
218
+ cargoManifest: `[package]\nname = "fixture"\nversion = "0.0.0"\nedition = "2021"\n[dependencies]\nrightkit-license = "=0.1.1"`,
219
+ cargoConfig: `[patch.crates-io]\nrightkit-license = { path = "../rightkit-license" }`,
220
+ });
221
+ const result = run(config, "--doctor");
222
+ assert.notEqual(result.status, 0);
223
+ assert.match(result.stderr, /forbidden RightKit Cargo override/i);
224
+ });
225
+
226
+ test("doctor rejects a RightKit override at an ancestor repository root", () => {
227
+ const config = fixture({
228
+ cargoManifest: `[package]\nname = "fixture"\nversion = "0.0.0"\nedition = "2021"\n[dependencies]\nrightkit-license = "=0.1.1"`,
229
+ repoCargoConfig: `[patch.crates-io]\nrightkit-license = { git = "https://example.test/rightkit-license.git" }`,
230
+ });
231
+ const result = run(config, "--doctor");
232
+ assert.notEqual(result.status, 0);
233
+ assert.match(result.stderr, /forbidden RightKit Cargo override/i);
234
+ });
235
+
174
236
  test("publish runs the signed package step before the updater publication step", () => {
175
237
  const result = run(fixture({ publish: true }), "--tier=update", "--upload");
176
238
  assert.equal(result.status, 0, result.stderr);
@@ -1,12 +1,21 @@
1
1
  import assert from "node:assert/strict";
2
- import { existsSync, readFileSync, readdirSync } from "node:fs";
2
+ import { existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
3
4
  import path from "node:path";
5
+ import { spawnSync } from "node:child_process";
4
6
  import { pathToFileURL } from "node:url";
5
- import test from "node:test";
7
+ import test, { after } from "node:test";
8
+ import {
9
+ assertNoRightKitCargoOverrides,
10
+ assertPublishedRightKitCargoDependencies,
11
+ validateRightKitCargoContract,
12
+ } from "./cargo-contract.mjs";
6
13
 
7
14
  const workspace = path.resolve(new URL("../../../..", import.meta.url).pathname.replace(/^\/(\w:)/, "$1"));
8
15
  const versionsPath = path.join(path.dirname(new URL(import.meta.url).pathname.replace(/^\/(\w:)/, "$1")), "rightkit-versions.json");
9
16
  const versions = JSON.parse(readFileSync(versionsPath, "utf8"));
17
+ const isolatedCargoHome = mkdtempSync(path.join(tmpdir(), "rightkit-cargo-home-"));
18
+ after(() => rmSync(isolatedCargoHome, { recursive: true, force: true }));
10
19
  const pubkey = "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDI5Mzk1RjlGRjQ2NjI2MUQKUldRZEptYjBuMTg1S1VSUXlBdFM4WmtzaHArYko0U2hRMDVlSDJmSExVZG82Q0hoQ2srUlhqanAK";
11
20
  const apps = [
12
21
  { key: "viewright", root: "viewright", tauri: "src-tauri/tauri.conf.json", releaseFiles: ["scripts/build-mac-notarized.sh"] },
@@ -16,19 +25,301 @@ const apps = [
16
25
  { key: "coderight", root: "coderight/apps/coderight-tauri", tauri: "src-tauri/tauri.conf.json", releaseFiles: ["scripts/mac-dmg.mjs"] },
17
26
  ];
18
27
 
28
+ function resolveCargoVersionContract(versionManifest, canonicalVersions) {
29
+ const consumer = new Map(Object.entries(versionManifest.cargo ?? {}));
30
+ const staged = new Map(Object.entries(versionManifest.stagedCargo ?? {}));
31
+ const canonical = new Map();
32
+
33
+ for (const [crate, version] of staged) {
34
+ if (!canonicalVersions.has(crate)) throw new Error(`unknown staged Cargo crate: ${crate}`);
35
+ const actual = canonicalVersions.get(crate);
36
+ if (actual !== version) {
37
+ throw new Error(`${crate} canonical manifest is ${actual}, expected staged version ${version}`);
38
+ }
39
+ canonical.set(crate, version);
40
+ }
41
+
42
+ for (const [crate, version] of consumer) {
43
+ if (!canonicalVersions.has(crate)) throw new Error(`unknown published Cargo crate: ${crate}`);
44
+ const expected = staged.get(crate) ?? version;
45
+ const actual = canonicalVersions.get(crate);
46
+ if (actual !== expected) {
47
+ const source = staged.has(crate) ? "staged" : "published";
48
+ throw new Error(`${crate} canonical manifest is ${actual}, expected ${source} version ${expected}`);
49
+ }
50
+ canonical.set(crate, expected);
51
+ }
52
+
53
+ return { canonical, consumer };
54
+ }
55
+
56
+ let currentCargoVersionContract;
57
+ function getCurrentCargoVersionContract() {
58
+ currentCargoVersionContract ??= resolveCargoVersionContract(versions, readCanonicalCrateVersions());
59
+ return currentCargoVersionContract;
60
+ }
61
+
62
+ const cargoContractCache = new Map();
63
+ function readCargoManifestContract(manifestPath, label) {
64
+ const cacheKey = path.resolve(manifestPath).toLowerCase();
65
+ if (cargoContractCache.has(cacheKey)) return cargoContractCache.get(cacheKey);
66
+ const result = spawnSync(
67
+ "cargo",
68
+ ["metadata", "--no-deps", "--offline", "--format-version", "1", "--manifest-path", manifestPath],
69
+ {
70
+ cwd: path.dirname(manifestPath),
71
+ encoding: "utf8",
72
+ env: { ...process.env, CARGO_HOME: isolatedCargoHome },
73
+ windowsHide: true,
74
+ },
75
+ );
76
+ assert.equal(
77
+ result.status,
78
+ 0,
79
+ `${label} Cargo metadata rejected manifest; RightKit dependencies must use exact crates.io versions: ${String(result.stderr ?? "").trim()}`,
80
+ );
81
+ const metadata = JSON.parse(result.stdout);
82
+ const expectedPath = path.resolve(manifestPath).toLowerCase();
83
+ const pkg = metadata.packages.find(({ manifest_path: candidate }) => path.resolve(candidate).toLowerCase() === expectedPath);
84
+ if (!pkg && path.resolve(metadata.workspace_root, "Cargo.toml").toLowerCase() === expectedPath) {
85
+ const contract = { dependencies: [], workspaceRoot: path.resolve(metadata.workspace_root) };
86
+ cargoContractCache.set(cacheKey, contract);
87
+ return contract;
88
+ }
89
+ assert.ok(pkg, `${label} was not returned by Cargo metadata`);
90
+ const contract = { dependencies: pkg.dependencies, workspaceRoot: path.resolve(metadata.workspace_root) };
91
+ cargoContractCache.set(cacheKey, contract);
92
+ return contract;
93
+ }
94
+
95
+ function assertCargoFixture(manifest, published, label) {
96
+ const fixtureRoot = mkdtempSync(path.join(tmpdir(), "rightkit-cargo-contract-"));
97
+ const manifestPath = path.join(fixtureRoot, "Cargo.toml");
98
+ const packageMetadata = /^\s*\[\s*package\s*\]/m.test(manifest)
99
+ ? ""
100
+ : '\n[package]\nname = "rightkit-contract-fixture"\nversion = "0.0.0"\nedition = "2021"\n';
101
+ writeFileSync(manifestPath, `${manifest}${packageMetadata}\n[lib]\npath = "fixture.rs"\n`, "utf8");
102
+ writeFileSync(path.join(fixtureRoot, "fixture.rs"), "", "utf8");
103
+ try {
104
+ return assertPublishedRightKitCargoDependencies(
105
+ readCargoManifestContract(manifestPath, label).dependencies,
106
+ published,
107
+ label,
108
+ );
109
+ } finally {
110
+ rmSync(fixtureRoot, { recursive: true, force: true });
111
+ }
112
+ }
113
+
114
+ function assertCargoOverrideFixture({ manifest, workspaceManifest, localConfig, ancestorConfig, outsideConfig }) {
115
+ const fixtureRoot = mkdtempSync(path.join(tmpdir(), "rightkit-cargo-override-"));
116
+ const repoRoot = path.join(fixtureRoot, "repo");
117
+ const appRoot = path.join(repoRoot, "app");
118
+ mkdirSync(appRoot, { recursive: true });
119
+ const manifestPath = path.join(appRoot, "Cargo.toml");
120
+ writeFileSync(manifestPath, manifest, "utf8");
121
+ if (workspaceManifest) writeFileSync(path.join(repoRoot, "Cargo.toml"), workspaceManifest, "utf8");
122
+ for (const [root, filename, source] of [
123
+ [appRoot, "config", localConfig],
124
+ [repoRoot, "config.toml", ancestorConfig],
125
+ [fixtureRoot, "config.toml", outsideConfig],
126
+ ]) {
127
+ if (!source) continue;
128
+ const cargoDir = path.join(root, ".cargo");
129
+ mkdirSync(cargoDir, { recursive: true });
130
+ writeFileSync(path.join(cargoDir, filename), source, "utf8");
131
+ }
132
+ try {
133
+ assertNoRightKitCargoOverrides(manifestPath, repoRoot, "fixture/Cargo.toml");
134
+ if (workspaceManifest) {
135
+ assertNoRightKitCargoOverrides(path.join(repoRoot, "Cargo.toml"), repoRoot, "fixture/workspace Cargo.toml");
136
+ }
137
+ return 0;
138
+ } finally {
139
+ rmSync(fixtureRoot, { recursive: true, force: true });
140
+ }
141
+ }
142
+
143
+ test("Cargo override contract accepts exact registry pins without repo-local overrides", () => {
144
+ assert.equal(assertCargoOverrideFixture({
145
+ manifest: `[package]\nname = "fixture"\nversion = "0.0.0"\n[dependencies]\nrightkit-license = "=0.1.1"`,
146
+ localConfig: `[net]\nretry = 2\n[registries.private]\nindex = "https://example.test/index"`,
147
+ outsideConfig: `[source.crates-io]\nreplace-with = "user-global-vendor"`,
148
+ }), 0);
149
+ });
150
+
151
+ test("Cargo override contract rejects RightKit patches and replacements in repo-local Cargo config", () => {
152
+ for (const config of [
153
+ { localConfig: `[patch.crates-io]\nrightkit-license = { path = "../rightkit-license" }` },
154
+ { localConfig: `patch . "https://github.com/rust-lang/crates.io-index" . "rightkit-logs" = { git = "https://example.test/rightkit-logs.git" }` },
155
+ { localConfig: `[ replace ]\n"rightkit-license:0.1.1" = { path = "../rightkit-license" }` },
156
+ { ancestorConfig: `[patch.private-source]\nadapter = { package = "rightkit-license", registry = "private" }` },
157
+ ]) {
158
+ assert.throws(
159
+ () => assertCargoOverrideFixture({
160
+ manifest: `[package]\nname = "fixture"\nversion = "0.0.0"\n[dependencies]\nrightkit-license = "=0.1.1"`,
161
+ ...config,
162
+ }),
163
+ /forbidden RightKit Cargo override/,
164
+ );
165
+ }
166
+ });
167
+
168
+ test("Cargo override contract rejects path patches, Git patches, and replace entries", () => {
169
+ for (const manifest of [
170
+ `patch . "crates-io" . "rightkit-license" = { path = "../rightkit-license" } # dotted path patch`,
171
+ `[ patch . "https://github.com/rust-lang/crates.io-index" ]\n"rightkit-logs" = { git = "https://example.test/rightkit-logs.git" }`,
172
+ `[ replace ]\n"rightkit-license:0.1.1" = { path = "../rightkit-license" }`,
173
+ ]) {
174
+ assert.throws(
175
+ () => assertCargoOverrideFixture({ manifest }),
176
+ /forbidden RightKit Cargo override/,
177
+ );
178
+ }
179
+ });
180
+
181
+ test("Cargo override contract rejects workspace-root patches inherited by a member", () => {
182
+ assert.throws(
183
+ () => assertCargoOverrideFixture({
184
+ manifest: `[package]\nname = "fixture"\nversion = "0.0.0"`,
185
+ workspaceManifest: `[workspace]\nmembers = ["app"]\n[patch.crates-io]\nrightkit-license = { path = "patched/rightkit-license" }`,
186
+ }),
187
+ /forbidden RightKit Cargo override/,
188
+ );
189
+ });
190
+
191
+ test("Cargo override contract rejects local and repo-ancestor crates.io source replacement", () => {
192
+ for (const fixture of [
193
+ {
194
+ localConfig: `source . crates-io . replace-with = "vendored-sources"\n[source.vendored-sources]\ndirectory = "vendor"`,
195
+ },
196
+ {
197
+ ancestorConfig: `[source."crates-io"]\nreplace-with = "rightkit-git"\n[source.rightkit-git]\ngit = "https://example.test/rightkit-index.git"`,
198
+ },
199
+ {
200
+ ancestorConfig: `[source.crates-io]\nreplace-with = "private-registry"\n[source.private-registry]\nregistry = "https://example.test/index"`,
201
+ },
202
+ ]) {
203
+ assert.throws(
204
+ () => assertCargoOverrideFixture({
205
+ manifest: `[package]\nname = "fixture"\nversion = "0.0.0"\n[dependencies]\nrightkit-license = "=0.1.1"`,
206
+ ...fixture,
207
+ }),
208
+ /forbidden crates\.io source replacement/,
209
+ );
210
+ }
211
+ });
212
+
213
+ test("direct Cargo dependency contract accepts only published exact RightKit pins", () => {
214
+ const published = new Map([
215
+ ["rightkit-license", "0.1.2"],
216
+ ["rightkit-logs", "0.1.0"],
217
+ ["rightkit-tauri", "0.1.0"],
218
+ ]);
219
+ const manifest = `
220
+ [ package ]
221
+ name = "rightkit-tauri"
222
+ # rightkit-tauri = "=0.1.0"
223
+
224
+ [ dependencies ]
225
+ "rightkit-license" = "=0.1.2" # published exact pin
226
+ "rightkit-tauri" = "=0.1.0"
227
+
228
+ [ target . 'cfg(windows)' . dependencies . "rightkit-logs" ]
229
+ version = "=0.1.0"
230
+
231
+ [ target . 'cfg(unix)' . dev-dependencies ]
232
+ license-adapter = { package = "rightkit-license", version = "=0.1.2" }
233
+ `;
234
+
235
+ assert.equal(assertCargoFixture(manifest, published, "fixture/Cargo.toml"), 4);
236
+ });
237
+
238
+ test("direct Cargo dependency contract rejects staged-only RightKit crates", () => {
239
+ const { consumer } = resolveCargoVersionContract(
240
+ {
241
+ cargo: { "rightkit-license": "0.1.2" },
242
+ stagedCargo: { "rightkit-tauri": "0.1.0" },
243
+ },
244
+ new Map([
245
+ ["rightkit-license", "0.1.2"],
246
+ ["rightkit-tauri", "0.1.0"],
247
+ ]),
248
+ );
249
+ assert.equal(consumer.has("rightkit-tauri"), false);
250
+ assert.throws(
251
+ () => assertCargoFixture(
252
+ `dependencies . "rightkit-tauri" = "=0.1.0" # staged only`,
253
+ consumer,
254
+ "fixture/Cargo.toml",
255
+ ),
256
+ /fixture\/Cargo\.toml rightkit-tauri is not a published RightKit crate/,
257
+ );
258
+ });
259
+
260
+ test("direct Cargo dependency contract still rejects path, Git, and custom registry sources", () => {
261
+ const published = new Map([
262
+ ["rightkit-license", "0.1.1"],
263
+ ["rightkit-logs", "0.1.0"],
264
+ ]);
265
+ for (const manifest of [
266
+ `dependencies . rightkit-license = { path = "../rightkit-license" }`,
267
+ `target . 'cfg(windows)' . dependencies . "rightkit-logs" = { git = "https://example.test/rightkit.git" }`,
268
+ `dependencies . rightkit-license = { version = "=0.1.1", registry = "private" }`,
269
+ ]) {
270
+ assert.throws(
271
+ () => assertCargoFixture(manifest, published, "fixture/Cargo.toml"),
272
+ /must use exact crates\.io versions|must use the exact crates\.io version/,
273
+ );
274
+ }
275
+ });
276
+
277
+ test("Cargo version contract keeps staged versions out of consumer pins", () => {
278
+ const resolved = resolveCargoVersionContract(
279
+ {
280
+ cargo: { "rightkit-license": "0.1.1" },
281
+ stagedCargo: { "rightkit-license": "0.1.2" },
282
+ },
283
+ new Map([["rightkit-license", "0.1.2"]]),
284
+ );
285
+
286
+ assert.equal(resolved.consumer.get("rightkit-license"), "0.1.1");
287
+ assert.equal(resolved.canonical.get("rightkit-license"), "0.1.2");
288
+ });
289
+
290
+ test("Cargo version contract rejects unknown staged crates", () => {
291
+ assert.throws(
292
+ () => resolveCargoVersionContract(
293
+ { cargo: {}, stagedCargo: { "rightkit-unknown": "0.1.0" } },
294
+ new Map(),
295
+ ),
296
+ /unknown staged Cargo crate: rightkit-unknown/,
297
+ );
298
+ });
299
+
300
+ test("Cargo version contract rejects staged versions that mismatch canonical manifests", () => {
301
+ assert.throws(
302
+ () => resolveCargoVersionContract(
303
+ { cargo: { "rightkit-license": "0.1.1" }, stagedCargo: { "rightkit-license": "0.1.3" } },
304
+ new Map([["rightkit-license", "0.1.2"]]),
305
+ ),
306
+ /rightkit-license canonical manifest is 0\.1\.2, expected staged version 0\.1\.3/,
307
+ );
308
+ });
309
+
19
310
  test("RightKit exposes one current version manifest", () => {
20
311
  assert.equal(existsSync(versionsPath), true, "rightkit-versions.json must be the single current-version source");
21
312
  assert.match(versions.packageManager, /^pnpm@\d+\.\d+\.\d+$/);
22
- assert.equal(versions.npm["@rightkit/release"], "0.2.18");
313
+ assert.equal(versions.npm["@rightkit/release"], "0.2.20");
23
314
  assert.equal(versions.npm["@rightkit/license"], "0.1.5");
24
315
  assert.equal(versions.npm["@rightkit/logs"], "0.1.3");
25
- assert.equal(versions.npm["@rightkit/updates"], "0.2.2");
26
- assert.equal(versions.cargo["rightkit-license"], "0.1.1");
316
+ assert.equal(versions.npm["@rightkit/tauri"], "0.1.0");
317
+ assert.equal(versions.npm["@rightkit/updates"], "0.2.3");
318
+ assert.equal(versions.cargo["rightkit-license"], "0.1.2");
27
319
  assert.equal(versions.cargo["rightkit-logs"], "0.1.0");
28
- for (const [crate, version] of Object.entries(versions.cargo)) {
29
- const manifest = readFileSync(path.join(workspace, `tools/rightkit/crates/${crate}/Cargo.toml`), "utf8");
30
- assert.match(manifest, new RegExp(`^version\\s*=\\s*"${version.replaceAll(".", "\\.")}"$`, "m"));
31
- }
320
+ assert.equal(versions.cargo["rightkit-tauri"], "0.1.0");
321
+ assert.deepEqual(versions.stagedCargo, {});
322
+ getCurrentCargoVersionContract();
32
323
  });
33
324
 
34
325
  test("license v2 public vector is identical at every portable consumer boundary", () => {
@@ -119,18 +410,8 @@ for (const app of apps) {
119
410
  ]);
120
411
  assert.equal(tauri.bundle.createUpdaterArtifacts, true);
121
412
 
122
- for (const manifestPath of findCargoManifests(root)) {
123
- const manifest = readFileSync(manifestPath, "utf8");
124
- for (const [crate, version] of Object.entries(versions.cargo)) {
125
- const dependency = manifest.match(new RegExp(`^${crate}\\s*=\\s*(.+)$`, "m"));
126
- if (!dependency) continue;
127
- assert.equal(
128
- dependency[1].trim(),
129
- `"=${version}"`,
130
- `${path.relative(workspace, manifestPath)} ${crate} must use the exact crates.io version`,
131
- );
132
- }
133
- }
413
+ const { consumer } = getCurrentCargoVersionContract();
414
+ validateRightKitCargoContract(root, consumer, app.key);
134
415
 
135
416
  for (const localCopy of [
136
417
  "src-tauri/vendor/rightkit-license",
@@ -173,7 +454,7 @@ function findFiles(root, filename) {
173
454
  const found = [];
174
455
  const visit = (dir) => {
175
456
  for (const entry of readdirSync(dir, { withFileTypes: true })) {
176
- if (entry.isDirectory() && [".git", ".claude", ".worktrees", "node_modules", "target", "vendor"].includes(entry.name)) continue;
457
+ if (entry.isDirectory() && [".cache", ".git", ".claude", ".worktrees", "node_modules", "target", "vendor"].includes(entry.name)) continue;
177
458
  const full = path.join(dir, entry.name);
178
459
  if (entry.isDirectory()) visit(full);
179
460
  else if (entry.isFile() && entry.name === filename) found.push(full);
@@ -183,7 +464,19 @@ function findFiles(root, filename) {
183
464
  return found;
184
465
  }
185
466
 
186
- const findCargoManifests = (root) => findFiles(root, "Cargo.toml");
467
+ function readCanonicalCrateVersions() {
468
+ const crateRoot = path.join(workspace, "tools/rightkit/crates");
469
+ return new Map(
470
+ readdirSync(crateRoot, { withFileTypes: true })
471
+ .filter((entry) => entry.isDirectory() && existsSync(path.join(crateRoot, entry.name, "Cargo.toml")))
472
+ .map((entry) => {
473
+ const manifest = readFileSync(path.join(crateRoot, entry.name, "Cargo.toml"), "utf8");
474
+ const version = manifest.match(/^version\s*=\s*"([^"]+)"$/m)?.[1];
475
+ assert.ok(version, `${entry.name} canonical manifest must declare a package version`);
476
+ return [entry.name, version];
477
+ }),
478
+ );
479
+ }
187
480
 
188
481
  test("Right Suite has no hosted workflow files", () => {
189
482
  for (const root of ["viewright", "scraperight", "heardright", "mailright", "coderight", "genright", "voiceright", "tools/rightkit"]) {
@@ -4,11 +4,14 @@
4
4
  "npm": {
5
5
  "@rightkit/license": "0.1.5",
6
6
  "@rightkit/logs": "0.1.3",
7
- "@rightkit/release": "0.2.18",
8
- "@rightkit/updates": "0.2.2"
7
+ "@rightkit/release": "0.2.20",
8
+ "@rightkit/tauri": "0.1.0",
9
+ "@rightkit/updates": "0.2.3"
9
10
  },
10
11
  "cargo": {
11
- "rightkit-license": "0.1.1",
12
- "rightkit-logs": "0.1.0"
13
- }
12
+ "rightkit-license": "0.1.2",
13
+ "rightkit-logs": "0.1.0",
14
+ "rightkit-tauri": "0.1.0"
15
+ },
16
+ "stagedCargo": {}
14
17
  }