@rightkit/release 0.2.83 → 0.2.85

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build-release.mjs CHANGED
@@ -76,12 +76,14 @@ const args = process.argv.slice(2);
76
76
  let configName = "right-release.config.mjs";
77
77
  let platform = process.platform === "win32" ? "win" : process.platform === "darwin" ? "mac" : process.platform;
78
78
  let dryRun = false;
79
+ let skipChecks = false;
79
80
 
80
81
  for (let i = 0; i < args.length; i += 1) {
81
82
  const arg = args[i];
82
83
  if (arg === "--config") configName = args[++i];
83
84
  else if (arg === "--platform") platform = args[++i];
84
85
  else if (arg === "--dry-run") dryRun = true;
86
+ else if (arg === "--skip-checks") skipChecks = true;
85
87
  else if (arg === "--tier" || arg.startsWith("--tier=")) fail("build is tier-neutral; select patch or update only during upload");
86
88
  else if (arg === "-h" || arg === "--help") usage(0);
87
89
  else fail(`unknown argument: ${arg}`);
@@ -337,7 +339,7 @@ try {
337
339
  throwIfInterrupted();
338
340
  await runProgress(
339
341
  process.execPath,
340
- [WORKER, "--config", configPath, "--platform", platform, "--no-upload"],
342
+ [WORKER, "--config", configPath, "--platform", platform, "--no-upload", ...(skipChecks ? ["--skip-checks"] : [])],
341
343
  appRoot,
342
344
  env,
343
345
  [managedCargoTarget ?? env.CARGO_TARGET_DIR, path.join(appRoot, ".cache")],
@@ -127,7 +127,7 @@ function printHelp() {
127
127
  console.log(`right-release <command> [options]
128
128
 
129
129
  Commands:
130
- build [--platform mac|win] Build, sign/notarize, harden, and seal; never uploads
130
+ build [--platform mac|win] [--skip-checks] Build, sign/notarize, harden, and seal; never uploads
131
131
  upload --release <id> --platform mac|win --tier patch|update
132
132
  Upload one sealed release; never builds
133
133
  release Compatibility alias for build
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rightkit/release",
3
- "version": "0.2.83",
3
+ "version": "0.2.85",
4
4
  "description": "Portable Right Suite release CLI/SDK: signed native archives, direct bootstrap transactions, immutable GitHub Release upload, R2 bootstrap publication, and add-on adoption.",
5
5
  "license": "MIT OR Apache-2.0",
6
6
  "type": "module",
@@ -17,6 +17,14 @@
17
17
  "!standalone-clone-evidence.json"
18
18
  ],
19
19
  "sideEffects": false,
20
+ "scripts": {
21
+ "test": "node --test --test-concurrency=1 --test-force-exit *.test.mjs",
22
+ "test:registry-parity": "node registry-parity.mjs --allow-unpublished",
23
+ "test:signer:windows": "node sign-release-manifest.integration.mjs",
24
+ "prepublishOnly": "pnpm test && pnpm test:signer:windows && pnpm test:registry-parity",
25
+ "doctor:all": "node --test right-suite-contract.test.mjs",
26
+ "verify:standalone": "node standalone-clone-verify.mjs"
27
+ },
20
28
  "publishConfig": {
21
29
  "registry": "https://registry.npmjs.org/",
22
30
  "access": "public"
@@ -26,11 +34,5 @@
26
34
  "url": "git+https://github.com/bogusyogi/claude.git",
27
35
  "directory": "tools/rightkit/packages/release"
28
36
  },
29
- "scripts": {
30
- "test": "node --test --test-concurrency=1 --test-force-exit *.test.mjs",
31
- "test:registry-parity": "node registry-parity.mjs --allow-unpublished",
32
- "test:signer:windows": "node sign-release-manifest.integration.mjs",
33
- "doctor:all": "node --test right-suite-contract.test.mjs",
34
- "verify:standalone": "node standalone-clone-verify.mjs"
35
- }
36
- }
37
+ "packageManager": "pnpm@11.24.0"
38
+ }
@@ -0,0 +1,5 @@
1
+ /** Run only app-configured pre-build checks; release safeguards never route here. */
2
+ export async function runConfiguredChecks({ packageManager, checks = [], workdir, skipChecks = false, run }) {
3
+ if (skipChecks) return;
4
+ for (const script of checks) await run(packageManager, script, workdir);
5
+ }
package/release.mjs CHANGED
@@ -23,6 +23,7 @@ import { verifyNsisEmbeddedBinary } from "./nsis-payload.mjs";
23
23
  import { terminateProcessTree } from "./heavy-command.mjs";
24
24
  import { materializeHardeningEvidence } from "./hardening-evidence.mjs";
25
25
  import { MACOS_PORTABLE_SIGNING_CONTRACT } from "./macos-signing-contract.mjs";
26
+ import { runConfiguredChecks } from "./release-checks.mjs";
26
27
 
27
28
  const TOOL_ROOT = path.dirname(fileURLToPath(import.meta.url));
28
29
  const HARDENING_SCAN = path.resolve(TOOL_ROOT, "hardeningscan.mjs");
@@ -217,9 +218,13 @@ const started = Date.now();
217
218
  console.log(`right-release ${VERSION}: ${config.app ?? path.basename(root)} ${opts.platform} ${mode} tier=${opts.tier ?? "neutral"}`);
218
219
 
219
220
  if (opts.install) await runInstall(config.packageManager, workdir);
220
- if (!opts.skipChecks) {
221
- for (const script of config.checks ?? []) await runPackageScript(config.packageManager, script, workdir);
222
- }
221
+ await runConfiguredChecks({
222
+ packageManager: config.packageManager,
223
+ checks: config.checks,
224
+ workdir,
225
+ skipChecks: opts.skipChecks,
226
+ run: runPackageScript,
227
+ });
223
228
 
224
229
  let nativeFinalization = null;
225
230
  let bundleMarkerReceipts = [];
@@ -671,14 +676,43 @@ async function validateHostedWorkflows(root, appName, workflows, policy) {
671
676
  if (!source.startsWith("# Managed by right-git")) {
672
677
  fail(`${appName} ${workflow} is not marked as right-git managed`);
673
678
  }
674
- const requestedSecrets = [...source.matchAll(/\bsecrets\.([A-Z0-9_]+)/gi)].map((match) => match[1]);
679
+ const jobSpan = (jobName) => {
680
+ const marker = `\n ${jobName}:\n`;
681
+ const start = source.indexOf(marker);
682
+ if (start < 0) return null;
683
+ const remainder = source.slice(start + marker.length);
684
+ const next = /\n [A-Za-z0-9_-]+:\n/.exec(remainder);
685
+ return { start, end: next ? start + marker.length + next.index : source.length };
686
+ };
687
+ const inside = (index, span) => span && index >= span.start && index < span.end;
688
+ const requestedSecrets = [...source.matchAll(/\bsecrets\.([A-Z0-9_]+)/gi)].map((match) => ({ name: match[1], index: match.index }));
675
689
  const allowedMacosSecrets = new Set(["APPLE_CERTIFICATE_BASE64", "APPLE_CERTIFICATE_PASSWORD", "APPLE_KEYCHAIN_PASSWORD", "APPLE_API_KEY_BASE64", "APPLE_API_KEY", "APPLE_API_ISSUER"]);
676
- const macosSigningAllowed = workflow === "release-candidate.yml" && manifest?.candidate?.signMacos === true;
690
+ const windowsSpan = jobSpan("windows-sign");
691
+ const macosSpan = jobSpan("macos-sign");
692
+ const publishSpan = jobSpan("publish");
693
+ const windowsSource = windowsSpan ? source.slice(windowsSpan.start, windowsSpan.end) : "";
694
+ const macosSource = macosSpan ? source.slice(macosSpan.start, macosSpan.end) : "";
695
+ const publishSource = publishSpan ? source.slice(publishSpan.start, publishSpan.end) : "";
696
+ const protectedWindowsSigning = workflow === "release-candidate.yml"
697
+ && manifest?.candidate?.signWindows === true
698
+ && /windows-sign:\n needs: \[candidate\]\n if: \$\{\{ startsWith\(github\.ref, 'refs\/tags\/v'\) \}\}/.test(windowsSource);
699
+ const protectedMacosSigning = workflow === "release-candidate.yml"
700
+ && manifest?.candidate?.signMacos === true
701
+ && /macos-sign:\n needs: \[candidate\]\n if: \$\{\{ startsWith\(github\.ref, 'refs\/tags\/v'\) \}\}/.test(macosSource);
702
+ const releaseChain = workflow === "release-candidate.yml" ? manifest?.candidate?.releaseChain : undefined;
703
+ const allowedReleaseSecrets = new Set(releaseChain?.publishSecrets ?? []);
704
+ const writePermissions = [...source.matchAll(/^\s*(actions|attestations|checks|contents|deployments|discussions|id-token|issues|models|packages|pages|pull-requests|security-events|statuses):\s*write\s*$/gmi)].map((match) => ({ name: match[1].toLowerCase(), index: match.index }));
705
+ const protectedReleasePublish = releaseChain
706
+ && /publish:\n needs: \[[^\n]*installed-qualification[^\n]*\]\n if: \$\{\{ startsWith\(github\.ref, 'refs\/tags\/v'\) && needs\.installed-qualification\.result == 'success'/.test(publishSource)
707
+ && /\n permissions:\n contents: write\n/.test(publishSource);
677
708
  if (/^\s*permissions:\s*write-all\s*$/mi.test(source)
678
- || /^\s*(?:actions|attestations|checks|contents|deployments|discussions|id-token|issues|models|packages|pages|pull-requests|security-events|statuses):\s*write\s*$/mi.test(source)
709
+ || writePermissions.some(({ name, index }) => name === "id-token"
710
+ ? !(protectedWindowsSigning && inside(index, windowsSpan))
711
+ : name !== "contents" || !protectedReleasePublish || !inside(index, publishSpan))
679
712
  || /pull_request_target\s*:/i.test(source)
680
713
  || /\bsecrets\s*\[/i.test(source)
681
- || requestedSecrets.some((name) => !macosSigningAllowed || !allowedMacosSecrets.has(name))) {
714
+ || requestedSecrets.some(({ name, index }) => !(protectedMacosSigning && inside(index, macosSpan) && allowedMacosSecrets.has(name))
715
+ && !(protectedReleasePublish && inside(index, publishSpan) && allowedReleaseSecrets.has(name)))) {
682
716
  fail(`${appName} right-git managed ${workflow} requests release-capable permissions or secrets`);
683
717
  }
684
718
  }
@@ -14,7 +14,7 @@
14
14
  },
15
15
  "stagedNpm": {
16
16
  "@rightkit/ax": "0.2.1",
17
- "@rightkit/git": "0.2.7",
17
+ "@rightkit/git": "0.2.14",
18
18
  "@rightkit/hooks": "0.1.1",
19
19
  "@rightkit/legal": "0.3.1",
20
20
  "@rightkit/legal-ui": "0.1.1",
@@ -22,7 +22,7 @@
22
22
  "@rightkit/logs": "0.1.4",
23
23
  "@rightkit/platform-ui": "0.1.1",
24
24
  "@rightkit/qa": "0.2.1",
25
- "@rightkit/release": "0.2.83",
25
+ "@rightkit/release": "0.2.85",
26
26
  "@rightkit/tauri": "0.1.1",
27
27
  "@rightkit/updates": "0.2.4"
28
28
  },
@@ -36,7 +36,8 @@
36
36
  "0.2.3",
37
37
  "0.2.4",
38
38
  "0.2.5",
39
- "0.2.6"
39
+ "0.2.6",
40
+ "0.2.7"
40
41
  ],
41
42
  "@rightkit/legal-ui": [
42
43
  "0.1.0"
@@ -84,7 +85,8 @@
84
85
  "0.2.74",
85
86
  "0.2.75",
86
87
  "0.2.76",
87
- "0.2.82"
88
+ "0.2.82",
89
+ "0.2.83"
88
90
  ],
89
91
  "@rightkit/qa": [
90
92
  "0.1.0",
@@ -122,8 +122,9 @@ export function materializeInTotoSlsaProvenance({
122
122
  sourceCommit,
123
123
  sourceRepository,
124
124
  subjects,
125
- startedAt,
126
- finishedAt = new Date().toISOString(),
125
+ now = () => new Date().toISOString(),
126
+ startedAt = now(),
127
+ finishedAt = now(),
127
128
  }) {
128
129
  assertIdentity({ product, version, target, sourceCommit });
129
130
  if (!outputPath.endsWith(".intoto.jsonl")) throw new Error("in-toto output must end in .intoto.jsonl");