@rightkit/release 0.2.11 → 0.2.12

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.
File without changes
package/deps.mjs CHANGED
File without changes
package/lsclean.sh CHANGED
File without changes
@@ -4,8 +4,9 @@ import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "nod
4
4
  import os from "node:os";
5
5
  import path from "node:path";
6
6
  import test from "node:test";
7
+ import { fileURLToPath } from "node:url";
7
8
 
8
- const helper = new URL("./mirror-root-artifact.mjs", import.meta.url);
9
+ const helper = fileURLToPath(new URL("./mirror-root-artifact.mjs", import.meta.url));
9
10
 
10
11
  test("mirrors a nested package artifact from a linked worktree into both package roots", () => {
11
12
  const temp = mkdtempSync(path.join(os.tmpdir(), "right-release-mirror-"));
@@ -26,7 +27,7 @@ test("mirrors a nested package artifact from a linked worktree into both package
26
27
  mkdirSync(path.dirname(source), { recursive: true });
27
28
  writeFileSync(source, "signed-dmg-fixture");
28
29
 
29
- execFileSync(process.execPath, [helper.pathname, "--file", source, "--package-root", packageRoot], { encoding: "utf8" });
30
+ execFileSync(process.execPath, [helper, "--file", source, "--package-root", packageRoot], { encoding: "utf8" });
30
31
 
31
32
  assert.equal(readFileSync(path.join(packageRoot, "App_1.0.0.dmg"), "utf8"), "signed-dmg-fixture");
32
33
  assert.equal(readFileSync(path.join(main, "apps", "desktop", "App_1.0.0.dmg"), "utf8"), "signed-dmg-fixture");
@@ -44,7 +45,7 @@ test("copies a main-worktree artifact into its package root", () => {
44
45
  mkdirSync(path.dirname(source), { recursive: true });
45
46
  writeFileSync(source, "main-worktree-dmg");
46
47
 
47
- execFileSync(process.execPath, [helper.pathname, "--file", source, "--package-root", root], { encoding: "utf8" });
48
+ execFileSync(process.execPath, [helper, "--file", source, "--package-root", root], { encoding: "utf8" });
48
49
 
49
50
  assert.equal(readFileSync(path.join(root, "App_2.0.0.dmg"), "utf8"), "main-worktree-dmg");
50
51
  } finally {
@@ -1,5 +1,6 @@
1
1
  import assert from "node:assert/strict";
2
2
  import os from "node:os";
3
+ import path from "node:path";
3
4
  import test from "node:test";
4
5
  import { notarytoolAuthArgs } from "./notary-auth.mjs";
5
6
 
@@ -9,7 +10,7 @@ test("prefers a complete App Store Connect API credential set", () => {
9
10
  process.env.APPLE_API_KEY_PATH = "~/AuthKey.p8";
10
11
  process.env.APPLE_API_KEY = "KEY123";
11
12
  process.env.APPLE_API_ISSUER = "ISSUER123";
12
- assert.deepEqual(notarytoolAuthArgs(), ["--key", `${os.homedir()}/AuthKey.p8`, "--key-id", "KEY123", "--issuer", "ISSUER123"]);
13
+ assert.deepEqual(notarytoolAuthArgs(), ["--key", path.join(os.homedir(), "AuthKey.p8"), "--key-id", "KEY123", "--issuer", "ISSUER123"]);
13
14
  } finally {
14
15
  process.env = before;
15
16
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rightkit/release",
3
- "version": "0.2.11",
3
+ "version": "0.2.12",
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": {
@@ -9,6 +9,7 @@
9
9
  "files": [
10
10
  "cli",
11
11
  "*.mjs",
12
+ "*.json",
12
13
  "*.sh",
13
14
  "*.py"
14
15
  ],
package/release.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { access, readFile } from "node:fs/promises";
2
+ import { access, readFile, readdir } from "node:fs/promises";
3
3
  import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
4
4
  import path from "node:path";
5
5
  import { fileURLToPath, pathToFileURL } from "node:url";
@@ -10,13 +10,10 @@ const HARDENING_SCAN = path.resolve(TOOL_ROOT, "hardeningscan.mjs");
10
10
  const UPLOAD_LARGE = path.resolve(TOOL_ROOT, "upload-large.mjs");
11
11
  const SIGN_WINDOWS = path.resolve(TOOL_ROOT, "sign-windows.mjs");
12
12
  const SIGN_UPDATER = path.resolve(TOOL_ROOT, "sign-updater.mjs");
13
- const VERSION = "0.2.11";
13
+ const RIGHTKIT_VERSIONS = JSON.parse(readFileSync(path.resolve(TOOL_ROOT, "rightkit-versions.json"), "utf8"));
14
+ const VERSION = RIGHTKIT_VERSIONS.npm["@rightkit/release"];
14
15
  const TIERS = new Set(["patch", "update"]);
15
- const RIGHTKIT_EXPECTED = new Map([
16
- ["@rightkit/license", "^0.1.5"],
17
- ["@rightkit/logs", "^0.1.3"],
18
- ["@rightkit/release", `^${VERSION}`],
19
- ]);
16
+ const RIGHTKIT_EXPECTED = new Map(Object.entries(RIGHTKIT_VERSIONS.npm));
20
17
  const FORBIDDEN_RIGHTKIT_SPEC = /^(?:git|file|link|workspace):|github|github\.com/i;
21
18
 
22
19
  const args = process.argv.slice(2);
@@ -181,6 +178,16 @@ async function validateRightKitPackageContract(root, appName) {
181
178
  const packageJsonPath = path.join(root, "package.json");
182
179
  const pkg = JSON.parse(await readFile(packageJsonPath, "utf8"));
183
180
  const scripts = pkg.scripts ?? {};
181
+ const workflowDir = path.join(root, ".github", "workflows");
182
+ if (existsSync(workflowDir)) {
183
+ const workflows = await readdir(workflowDir);
184
+ if (workflows.length) {
185
+ fail(`${appName ?? pkg.name ?? "app"} hosted workflow files are forbidden: ${workflows.join(", ")}`);
186
+ }
187
+ }
188
+ if (pkg.packageManager !== RIGHTKIT_VERSIONS.packageManager) {
189
+ fail(`${appName ?? pkg.name ?? "app"} packageManager must be ${RIGHTKIT_VERSIONS.packageManager}, got ${pkg.packageManager ?? "<missing>"}`);
190
+ }
184
191
  if (JSON.stringify(scripts).includes("../tools/right-release") || JSON.stringify(scripts).includes("../tools/rightkit/packages/release")) {
185
192
  fail(`${appName ?? pkg.name ?? "app"} package.json must call the right-release bin, not parent-workspace release source`);
186
193
  }
package/release.test.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import assert from "node:assert/strict";
2
- import { existsSync, mkdtempSync, writeFileSync } from "node:fs";
2
+ import { existsSync, mkdirSync, mkdtempSync, readFileSync, writeFileSync } from "node:fs";
3
3
  import os from "node:os";
4
4
  import path from "node:path";
5
5
  import { spawnSync } from "node:child_process";
@@ -7,6 +7,7 @@ import { fileURLToPath } from "node:url";
7
7
  import test from "node:test";
8
8
 
9
9
  const release = fileURLToPath(new URL("./release.mjs", import.meta.url));
10
+ const versions = JSON.parse(readFileSync(new URL("./rightkit-versions.json", import.meta.url), "utf8"));
10
11
 
11
12
  function fixture({ signed = true, publish = false, packageJson } = {}) {
12
13
  const dir = mkdtempSync(path.join(os.tmpdir(), "right-release-test-"));
@@ -17,8 +18,12 @@ function fixture({ signed = true, publish = false, packageJson } = {}) {
17
18
  packageJson ?? {
18
19
  name: "fixture",
19
20
  scripts: { "release:patch:win": "right-release --platform win --tier patch" },
20
- dependencies: { "@rightkit/license": "^0.1.5", "@rightkit/logs": "^0.1.3" },
21
- devDependencies: { "@rightkit/release": "^0.2.11" },
21
+ packageManager: versions.packageManager,
22
+ dependencies: {
23
+ "@rightkit/license": versions.npm["@rightkit/license"],
24
+ "@rightkit/logs": versions.npm["@rightkit/logs"],
25
+ },
26
+ devDependencies: { "@rightkit/release": versions.npm["@rightkit/release"] },
22
27
  },
23
28
  null,
24
29
  2,
@@ -56,8 +61,12 @@ function lockFixture() {
56
61
  {
57
62
  name: "fixture-lock",
58
63
  scripts: { "release:patch:mac": "right-release --platform mac --tier patch" },
59
- dependencies: { "@rightkit/license": "^0.1.5", "@rightkit/logs": "^0.1.3" },
60
- devDependencies: { "@rightkit/release": "^0.2.11" },
64
+ packageManager: versions.packageManager,
65
+ dependencies: {
66
+ "@rightkit/license": versions.npm["@rightkit/license"],
67
+ "@rightkit/logs": versions.npm["@rightkit/logs"],
68
+ },
69
+ devDependencies: { "@rightkit/release": versions.npm["@rightkit/release"] },
61
70
  },
62
71
  null,
63
72
  2,
@@ -134,6 +143,7 @@ test("rejects Git, path, or link RightKit app dependencies before release work s
134
143
  fixture({
135
144
  packageJson: {
136
145
  name: "fixture",
146
+ packageManager: versions.packageManager,
137
147
  scripts: { "release:patch:win": "node ../tools/right-release/release.mjs --tier patch" },
138
148
  dependencies: {
139
149
  "@rightkit/license": "git+https://github.com/adrdsouza/rightkit.git#main",
@@ -150,6 +160,17 @@ test("rejects Git, path, or link RightKit app dependencies before release work s
150
160
  assert.match(result.stderr, /right-release bin|published npm package/i);
151
161
  });
152
162
 
163
+ test("rejects hosted workflow files before release work starts", () => {
164
+ const config = fixture();
165
+ const workflowDir = path.join(path.dirname(config), ".github", "workflows");
166
+ mkdirSync(workflowDir, { recursive: true });
167
+ writeFileSync(path.join(workflowDir, "ci.yml"), "name: forbidden\n");
168
+
169
+ const result = run(config, "--tier=patch");
170
+ assert.notEqual(result.status, 0);
171
+ assert.match(result.stderr, /hosted workflow.*forbidden/i);
172
+ });
173
+
153
174
  test("publish runs the signed package step before the updater publication step", () => {
154
175
  const result = run(fixture({ publish: true }), "--tier=update", "--upload");
155
176
  assert.equal(result.status, 0, result.stderr);
@@ -1,22 +1,30 @@
1
1
  import assert from "node:assert/strict";
2
- import { existsSync, readFileSync } from "node:fs";
2
+ import { existsSync, readFileSync, readdirSync } from "node:fs";
3
3
  import path from "node:path";
4
4
  import { pathToFileURL } from "node:url";
5
5
  import test from "node:test";
6
6
 
7
7
  const workspace = path.resolve(new URL("../../../..", import.meta.url).pathname.replace(/^\/(\w:)/, "$1"));
8
+ const versionsPath = path.join(path.dirname(new URL(import.meta.url).pathname.replace(/^\/(\w:)/, "$1")), "rightkit-versions.json");
9
+ const versions = JSON.parse(readFileSync(versionsPath, "utf8"));
8
10
  const pubkey = "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDI5Mzk1RjlGRjQ2NjI2MUQKUldRZEptYjBuMTg1S1VSUXlBdFM4WmtzaHArYko0U2hRMDVlSDJmSExVZG82Q0hoQ2srUlhqanAK";
9
- const licensePackage = "^0.1.5";
10
- const logsPackage = "^0.1.3";
11
- const releasePackage = "^0.2.11";
12
11
  const apps = [
13
12
  { key: "viewright", root: "viewright", tauri: "src-tauri/tauri.conf.json", releaseFiles: ["scripts/build-mac-notarized.sh"] },
14
13
  { key: "scraperight", root: "scraperight", tauri: "src-tauri/tauri.conf.json", releaseFiles: ["package.sh"] },
15
14
  { key: "heardright", root: "heardright/tauri-app-next", tauri: "src-tauri/tauri.conf.json", releaseFiles: ["scripts/mac-dmg.mjs", "scripts/publish-release.mjs"] },
16
- { key: "mailright", root: "mailright", tauri: "src-tauri/tauri.conf.json", releaseFiles: ["scripts/mac-dmg.mjs"] },
15
+ { key: "mailright", root: "mailright", tauri: "src-tauri/tauri.conf.json", releaseFiles: ["scripts/build-mac.sh"] },
17
16
  { key: "coderight", root: "coderight/apps/coderight-tauri", tauri: "src-tauri/tauri.conf.json", releaseFiles: ["scripts/mac-dmg.mjs"] },
18
17
  ];
19
18
 
19
+ test("RightKit exposes one current version manifest", () => {
20
+ assert.equal(existsSync(versionsPath), true, "rightkit-versions.json must be the single current-version source");
21
+ assert.match(versions.packageManager, /^pnpm@\d+\.\d+\.\d+$/);
22
+ assert.equal(versions.npm["@rightkit/release"], "0.2.12");
23
+ assert.equal(versions.npm["@rightkit/license"], "0.1.5");
24
+ assert.equal(versions.npm["@rightkit/logs"], "0.1.3");
25
+ assert.equal(versions.npm["@rightkit/updates"], "0.1.1");
26
+ });
27
+
20
28
  for (const app of apps) {
21
29
  test(`${app.key} follows the signed tiered Right Release contract`, async () => {
22
30
  const root = path.join(workspace, app.root);
@@ -24,16 +32,16 @@ for (const app of apps) {
24
32
  const rightKitDeps = { ...pkg.dependencies, ...pkg.devDependencies };
25
33
  for (const [name, specifier] of Object.entries(rightKitDeps).filter(([name]) => name.startsWith("@rightkit/"))) {
26
34
  assert.doesNotMatch(specifier, /^(?:git|file|link|workspace):|github/i, `${app.key} ${name} must come from the published npm package`);
35
+ if (versions.npm[name]) assert.equal(specifier, versions.npm[name], `${app.key} ${name} must match rightkit-versions.json exactly`);
27
36
  }
28
- if (rightKitDeps["@rightkit/license"]) assert.equal(rightKitDeps["@rightkit/license"], licensePackage);
29
- if (rightKitDeps["@rightkit/logs"]) assert.equal(rightKitDeps["@rightkit/logs"], logsPackage);
30
- assert.equal(pkg.devDependencies?.["@rightkit/release"], releasePackage);
37
+ assert.equal(pkg.packageManager, versions.packageManager);
38
+ assert.equal(pkg.devDependencies?.["@rightkit/release"], versions.npm["@rightkit/release"]);
31
39
  assert(!JSON.stringify(pkg).includes("github:adrdsouza/claude#main&path:/tools/right-release"));
32
40
  assert(!JSON.stringify(pkg).includes("github:adrdsouza/claude#main&path:/tools/rightkit/packages/release"));
33
41
  assert(!JSON.stringify(pkg).includes("git+https://github.com/adrdsouza/rightkit.git"));
34
42
  assert.equal(pkg.scripts["release:doctor"], "right-release doctor");
35
- assert.equal(pkg.scripts["release:mac"], "right-release --platform mac");
36
- assert.equal(pkg.scripts["release:win"], "right-release --platform win");
43
+ assert.equal(pkg.scripts["release:mac"], undefined, "tierless release entry points are forbidden");
44
+ assert.equal(pkg.scripts["release:win"], undefined, "tierless release entry points are forbidden");
37
45
  assert.equal(pkg.scripts["release:patch:mac"], "right-release --platform mac --tier patch");
38
46
  assert.equal(pkg.scripts["release:patch:win"], "right-release --platform win --tier patch");
39
47
  assert.equal(pkg.scripts["release:update:mac"], "right-release --platform mac --tier update");
@@ -44,7 +52,7 @@ for (const app of apps) {
44
52
  assert.equal(pkg.scripts["publish:update:win"], "right-release publish --platform win --tier update");
45
53
  assert.equal(pkg.scripts["deps:check"], "right-release deps --check");
46
54
  assert.equal(pkg.scripts["deps:update"], "right-release deps --update");
47
- assert.ok(!Object.entries(pkg.scripts).some(([name, command]) => /mac|dmg/i.test(name) && /unsigned|--no-sign/i.test(command)), `${app.key} must not expose an unsigned macOS DMG mode`);
55
+ assert.ok(!Object.entries(pkg.scripts).some(([name, command]) => /^(?:release|publish):/i.test(name) && /unsigned|--no-sign/i.test(command)), `${app.key} release/publish commands must not expose an unsigned macOS DMG mode`);
48
56
  assert.ok(!JSON.stringify(pkg.scripts).includes("../tools/right-release"), `${app.key} scripts must not depend on the parent Claude workspace`);
49
57
  assert.ok(!JSON.stringify(pkg.scripts).includes("../tools/rightkit/packages/release"), `${app.key} scripts must not depend on the parent Claude workspace`);
50
58
 
@@ -66,7 +74,8 @@ for (const app of apps) {
66
74
  assert.match(updater.key, /\/updates\/(mac|windows)\/current\//, `${app.key} ${platform} updaters must replace the stable current R2 object`);
67
75
  }
68
76
  }
69
- assert.match(config.targets.mac.package.args.join(" "), /mac:dmg:notarized|--notarize/, `${app.key} publish packaging must use the notarized macOS lane`);
77
+ assert.equal(config.targets.mac.package.cmd, "pnpm", `${app.key} must use the shared pnpm package entry point`);
78
+ assert.deepEqual(config.targets.mac.package.args, ["run", "mac:dmg:notarized"], `${app.key} R2 release must use the signed and notarized macOS package entry point`);
70
79
  for (const installer of config.targets.mac.installer.artifacts) {
71
80
  assert.equal(path.dirname(installer.file), ".", `${app.key} macOS installer must be copied to the app package root before upload`);
72
81
  }
@@ -85,12 +94,17 @@ for (const app of apps) {
85
94
  assert.doesNotMatch(source, /(?:\.\.\/)+tools\/right-release|tools\/right-release\//, `${app.key} ${releaseFile} must consume the installed package`);
86
95
  assert.match(source, /right-release["']?,?\s*["']mirror-root-artifact|right-release mirror-root-artifact/, `${app.key} ${releaseFile} must mirror the final DMG to the canonical package root`);
87
96
  }
88
- for (const forbidden of ["scripts/build-mac-unsigned.sh", "package-unsigned.sh"]) {
89
- assert.equal(existsSync(path.join(root, forbidden)), false, `${app.key} must not ship ${forbidden}`);
90
- }
91
97
  });
92
98
  }
93
99
 
100
+ test("Right Suite has no hosted workflow files", () => {
101
+ for (const root of ["viewright", "scraperight", "heardright", "mailright", "coderight", "genright", "voiceright", "tools/rightkit"]) {
102
+ const workflowDir = path.join(workspace, root, ".github", "workflows");
103
+ const workflows = existsSync(workflowDir) ? readdirSync(workflowDir) : [];
104
+ assert.deepEqual(workflows, [], `${root} must not contain hosted workflow files`);
105
+ }
106
+ });
107
+
94
108
  test("RightApps brand hosts expose app-keyed update manifest proxies", () => {
95
109
  for (const app of apps) {
96
110
  const siteRoot = path.join(workspace, "rightapps", app.key);
@@ -0,0 +1,10 @@
1
+ {
2
+ "schema": 1,
3
+ "packageManager": "pnpm@11.12.0",
4
+ "npm": {
5
+ "@rightkit/license": "0.1.5",
6
+ "@rightkit/logs": "0.1.3",
7
+ "@rightkit/release": "0.2.12",
8
+ "@rightkit/updates": "0.1.1"
9
+ }
10
+ }
package/upload-large.mjs CHANGED
File without changes