@rightkit/release 0.6.16 → 0.6.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.
Files changed (64) hide show
  1. package/README.md +7 -3
  2. package/admission-host.mjs +9 -5
  3. package/app-closure.mjs +9 -3
  4. package/bin/darwin/rightkit-swap +0 -0
  5. package/bin/rightkit-gate.mjs +469 -102
  6. package/bin/rightkit-linux-setup.mjs +15 -0
  7. package/broker-admission.mjs +184 -0
  8. package/broker-wire.mjs +15 -3
  9. package/build-launch.mjs +4 -1
  10. package/build-ownership.mjs +125 -36
  11. package/build-release.mjs +39 -7
  12. package/cargo-contract.mjs +4 -1
  13. package/cargo-guard.mjs +25 -5
  14. package/cargo-target-shim.mjs +56 -7
  15. package/clippy-route.mjs +8 -7
  16. package/crate-graph.mjs +5 -1
  17. package/credential-provider.mjs +174 -0
  18. package/deterministic-scans.mjs +247 -40
  19. package/development.mjs +8 -0
  20. package/disk-policy.mjs +2 -2
  21. package/gate-executor.mjs +5 -3
  22. package/gate-stages.mjs +34 -3
  23. package/gradle-lock.mjs +206 -0
  24. package/heavy-command.mjs +33 -2
  25. package/linux-artifacts.mjs +2 -1
  26. package/linux-setup.mjs +106 -0
  27. package/local-publish.mjs +21 -10
  28. package/local-qualify.mjs +9 -3
  29. package/managed-cargo.mjs +54 -18
  30. package/ownership-lockfiles.mjs +14 -3
  31. package/ownership.mjs +5 -1
  32. package/package.json +17 -11
  33. package/protected-credentials.mjs +13 -5
  34. package/publication-gate.mjs +9 -4
  35. package/python-lock.mjs +209 -0
  36. package/qa-admission-policy.json +32 -0
  37. package/qa-layout.mjs +229 -0
  38. package/qa-runner.mjs +156 -0
  39. package/release-artifact-path.mjs +29 -3
  40. package/release-config.mjs +1 -1
  41. package/release-qa-command.mjs +58 -8
  42. package/release-qa-policy.mjs +5 -3
  43. package/release-source-identity.mjs +1 -0
  44. package/release.mjs +10 -2
  45. package/rightkit-ownership.json +1465 -79
  46. package/rightkit-registry-resolver.mjs +7 -3
  47. package/rightkit-versions.json +173 -36
  48. package/runtime-budget-classes.json +4 -0
  49. package/runtime-budgets.mjs +5 -1
  50. package/shell-contract.mjs +1 -1
  51. package/snapshot-broker.mjs +107 -9
  52. package/snapshot-check.mjs +2 -0
  53. package/snapshot-request.mjs +20 -3
  54. package/snapshot.mjs +11 -4
  55. package/stage-commands.mjs +2 -1
  56. package/stage-ledger.mjs +15 -0
  57. package/stage-sidecar.mjs +17 -3
  58. package/suite-signing.mjs +40 -4
  59. package/swift-lock.mjs +334 -0
  60. package/test-command.mjs +13 -3
  61. package/tool-build.mjs +156 -0
  62. package/unit-gate.mjs +9 -2
  63. package/windows-identity-plan.mjs +119 -0
  64. package/windows-identity.mjs +136 -0
package/README.md CHANGED
@@ -220,7 +220,7 @@ Adrian, 2026-10-26: the apps lost every build route on 2026-10-25 and build agai
220
220
 
221
221
  ## The gate on a host without the broker, and tests that reach it
222
222
 
223
- The gate chooses its executor itself (`gate-executor.mjs` `resolveExecutor`), never the caller: the RustRight broker on a managed Mac or Dell (chosen by host identity, not by `rustright` on PATH), a refusal on a broker-managed machine whose broker cannot be reached, and direct `cargo` on every Linux host and any other host (a cloud session, a CI runner). Every host runs the same gates in the same order (app rules, class A, then the compile scans); an unmanaged host is never sent to a broker and nothing is deferred to one. Commit and push run no gate and no local hook check, and `install-hook` writes no git hook (below); when git's ref lines are nevertheless given on stdin (the form a pre-push hook receives), every ref is judged in any position (split on single ASCII spaces, so a Unicode space in a ref name cannot hide one), and a line that is not four fields is refused, never dropped, so the pushed commit is always compared with HEAD.
223
+ The gate chooses its executor itself (`gate-executor.mjs` `resolveExecutor`), never the caller: the RustRight broker on a managed Mac or Dell (chosen by host identity, not by `rustright` on PATH), a refusal on a broker-managed machine whose broker cannot be reached, and direct `cargo` on every Linux host and any other host (a cloud session, a CI runner). `rightkit-linux-setup` (`linux-setup.mjs`: `readPins`, `setup`) provisions such a Linux host from the app's pins. Every host runs the same gates in the same order (app rules, class A, then the compile scans); an unmanaged host is never sent to a broker and nothing is deferred to one. Commit and push run no gate and no local hook check, and `install-hook` writes no git hook (below); when git's ref lines are nevertheless given on stdin (the form a pre-push hook receives), every ref is judged in any position (split on single ASCII spaces, so a Unicode space in a ref name cannot hide one), and a line that is not four fields is refused, never dropped, so the pushed commit is always compared with HEAD.
224
224
 
225
225
  `scriptBrokerCall` reads `package.json` scripts only: it follows `pnpm <script>`, `pnpm run`, `npm run`, `yarn` and the wrapper words, not `node -e`, a node script, `sh -c`, or a run in another package (`pnpm --dir`, `--filter`, `-r`). A unit whose test reaches the broker that way says so in the app's own `ci/gate.config.json`, in the `npm` entry the unit's `quietTests` already uses (the key is the unit name: the package name, or the directory with `/` as `-` for a unit of a pnpm root below the repository root):
226
226
 
@@ -231,6 +231,10 @@ The gate chooses its executor itself (`gate-executor.mjs` `resolveExecutor`), ne
231
231
 
232
232
  The `reason` is required and is printed. The file is read from the gated commit, so the declaration is a reviewed change in the diff that adds it. Where the Rust gate is deferred, the unit's `test:npm:<unit>` scan is deferred with it and named `declared in ci/gate.config.json npm.<unit>.requiresBroker: <reason>`, never reported as passed; on a host with the broker, in CI, on a protected ref and with the opt-out it runs as it did. A declaration of any other shape (`true`, a string, an empty or blank `reason`, an array) is ignored with a note and the test runs where it can: a malformed entry never skips a test. `false` or `null` is no declaration, and a call the scripts do show is reported from the scripts. The gate does not read node scripts for a literal `rustright` or `cargo`: a literal cannot tell a spawn from a command table, a mention or a test fixture, so the declaration is the way.
233
233
 
234
+ ## Tests: unit class and broker class
235
+
236
+ `test-support/suites.json` declares every `*.test.mjs` of this package as `unit` or `broker`. `pnpm test` runs the **unit** class: it needs nothing installed beyond Node, git and a Rust toolchain, and passes with no skip on a Linux host with no RustRight broker (the qualify-direct gate runs exactly this). The **broker** class (`pnpm run test:broker`) is the files that start the installed RustRight broker through `test-support/real-broker.mjs`; it runs only where a broker is installed (Mac, Dell, or `rustright` on PATH), stops with an error where there is none, and is never skipped or simulated. `pnpm run test:all` runs both. **`pnpm run test:broker` on a Mac or Dell is required evidence before this package is published.** `suites.test.mjs` fails when a test file is missing from the manifest or a unit file imports `real-broker.mjs`. The shard runner also removes the gate's own variables (build ownership, RustRight carrier) from the test processes, so a run inside a gate stage behaves as a shell run does.
237
+
234
238
  ## `install-hook` writes no git hook, and retires the ones an earlier release wrote
235
239
 
236
240
  Commit and push run no gate and no local hook check (`docs/GATE-CONTRACT.md`: the gate is for building; CI runs preflight and the full check on every push). `rightkit-gate install-hook` (a `prepare` script; `right-release hooks install` is an alias) registers the one-door agent hook in `.claude/settings.json` and writes no git hook: no `.githooks/pre-push`, no forwarder for the hooks in `.git/hooks`, no pre-commit ownership step, and it no longer sets `core.hooksPath`.
@@ -541,9 +545,9 @@ On the Mac and the Dell the gate refuses a gate-run suite or dev build whose con
541
545
 
542
546
  ## Entry points
543
547
 
544
- Bins: `right-release` (`cli/right-release.mjs`) and `rightkit-gate` (`bin/rightkit-gate.mjs`). The CLI is the supported surface.
548
+ Bins: `right-release` (`cli/right-release.mjs`), `rightkit-gate` (`bin/rightkit-gate.mjs`), `rightkit-linux-setup` (`bin/rightkit-linux-setup.mjs`) and `rightkit-agent-hook` (`bin/rightkit-agent-hook.mjs`). The CLI is the supported surface.
545
549
 
546
- `package.json` carries an explicit `exports` map (plan item 2.10, C4-16): one `"./<file>"` entry per public file, no `"."` index, no wildcard. It lists the two bin files, `./package.json`, the modules this README documents as SDK (`./release-dependencies.mjs`, `./engine-locator.mjs`, `./pe-content.mjs`, `./dmg-build.mjs`, `./door.mjs`) and the modules that product repositories and RightKit's own tooling import by deep path today. Every other file is private: `import "@rightkit/release/release.mjs"` fails with `ERR_PACKAGE_PATH_NOT_EXPORTED`, and so does `import "@rightkit/release"`. `docs/RELEASE-EXPORTS.md` in the RightKit repository is the inventory (every consumer import with file:line) and `release-exports.test.mjs` fails when the map, that inventory or a checked-out product repository disagree. A new public subpath needs an inventory row or a README SDK section in the same commit; the signing, notarization and packaging modules in the map are a shrinking baseline, not an invitation. A file read by path (`node_modules/@rightkit/release/<file>`) is outside the map and is listed in the inventory too.
550
+ `package.json` carries an explicit `exports` map (plan item 2.10, C4-16): one `"./<file>"` entry per public file, no `"."` index, no wildcard. It lists the bin files, `./package.json`, the modules this README documents as SDK (`./release-dependencies.mjs`, `./engine-locator.mjs`, `./pe-content.mjs`, `./dmg-build.mjs`, `./door.mjs`) and the modules that product repositories and RightKit's own tooling import by deep path today. Every other file is private: `import "@rightkit/release/release.mjs"` fails with `ERR_PACKAGE_PATH_NOT_EXPORTED`, and so does `import "@rightkit/release"`. `docs/RELEASE-EXPORTS.md` in the RightKit repository is the inventory (every consumer import with file:line) and `release-exports.test.mjs` fails when the map, that inventory or a checked-out product repository disagree. A new public subpath needs an inventory row or a README SDK section in the same commit; the signing, notarization and packaging modules in the map are a shrinking baseline, not an invitation. A file read by path (`node_modules/@rightkit/release/<file>`) is outside the map and is listed in the inventory too.
547
551
 
548
552
  ## Dependencies
549
553
 
@@ -5,6 +5,7 @@
5
5
  import { readFileSync } from "node:fs";
6
6
  import path from "node:path";
7
7
  import { brokerManagedHost } from "./cargo-contract.mjs";
8
+ import { CredentialProviderError, providerPlatformOf, updaterCredentialProvider } from "./credential-provider.mjs";
8
9
  import { ManagedHostEnvSecretError, isManagedHost } from "./managed-host-secrets.mjs";
9
10
  import { resolveMacosDeveloperIdIdentity } from "./macos-signing-identity.mjs";
10
11
  import { notarytoolAuthArgs } from "./notary-auth.mjs";
@@ -215,14 +216,17 @@ export const HOST_ROWS = Object.freeze([
215
216
  },
216
217
  },
217
218
  {
218
- // protected-credentials.mjs execWithUpdaterCredential: the Windows updater key is read by rightkit-credentials, not by this tool.
219
+ // protected-credentials.mjs execWithUpdaterCredential: the updater key is read by the ENROLLED rightkit-credentials (credential-provider.mjs), not by this tool.
220
+ // A Mac or Windows host resolves it from its managed data root and re-hashes it; the finding is the very refusal execWithUpdaterCredential raises.
219
221
  id: "host-updater-credential",
220
222
  tier: "host",
221
223
  run(ctx) {
222
- if (ctx.platform !== "win" || !signsUpdater(ctx)) return { na: "the target does not sign updater artifacts on Windows" };
223
- if (ctx.hostPlatform !== "win32") return { notRun: "the updater credential provider is probed on the Windows host that signs" };
224
- const result = probe(ctx, "rightkit-credentials", ["--version"]);
225
- if (!result.found || result.error?.code === "ENOENT") return ["OS credential provider unavailable: install rightkit-credentials >=0.2.2 on PATH before updater signing"];
224
+ if (!signsUpdater(ctx)) return { na: "the target does not sign updater artifacts" };
225
+ if (providerPlatformOf(ctx.hostPlatform) !== ctx.platform) return { notRun: `the updater credential provider is resolved on the ${ctx.platform === "win" ? "Windows" : "Mac"} host that signs` };
226
+ try { updaterCredentialProvider({ env: ctx.env, hostPlatform: ctx.hostPlatform }); } catch (error) {
227
+ if (error instanceof CredentialProviderError) return [error.message];
228
+ throw error;
229
+ }
226
230
  return undefined;
227
231
  },
228
232
  },
package/app-closure.mjs CHANGED
@@ -1,4 +1,5 @@
1
- // Executing scans follow the selected unit's committed dependency closure. File-only scans stay repository-wide.
1
+ // Executing scans follow the selected unit's committed dependency closure. File-only scans stay repository-wide. A pnpm root's lockfile
2
+ // install and audit are workspace scans (they read that root's whole install graph and the network advisory data): they follow the closure.
2
3
  // A Cargo scan (metadata, package list, deny, audit) executes cargo, through the broker on a broker host: it is an executing scan,
3
4
  // never a file-only one, and follows the Rust closure (no Rust unit in the closure, no Cargo scan).
4
5
  // Unknown local dependency edges refuse; they never silently omit checks or authorize every repository unit.
@@ -169,7 +170,10 @@ export const isCargoScan = (s) => CARGO_WORKSPACE_SCAN.test(String(s.id)) || CAR
169
170
  export const isExecutingScan = (s) => s.executes === true || s.kind === "test" || s.id === "suite-api" || isCargoScan(s);
170
171
 
171
172
  /** Keep file-only checks; filter every executing unit check (Cargo scans included) & refuse unclassified repository execution. */
172
- export function scopeScans(scans, closure, { rustUnits = [] } = {}) {
173
+ export function scopeScans(scans, closure, { rustUnits = [], npmUnits = [] } = {}) {
174
+ // The pnpm roots that hold a unit of the closure: a root's lockfile install and audit (pnpm-lockfile[:<root>], pnpm-audit[:<root>])
175
+ // read that root's whole install graph, so they run only for these (GC-A03). A Rust-only closure, or a sample app's own root, has none.
176
+ const closureNpmRoots = new Set(npmUnits.filter((u) => closure.npm.has(u.name)).map((u) => cleanWorkspace(u.root ?? ".")));
173
177
  const names = rustUnits.map((u) => u.name).sort((a, b) => b.length - a.length);
174
178
  // The Cargo workspaces that hold a unit of the closure: a workspace-level Cargo scan (metadata, deny, audit) runs only for these.
175
179
  const closureWorkspaces = new Set(rustUnits.filter((u) => closure.rust.has(u.name)).map((u) => cleanWorkspace(u.cargoDir)));
@@ -179,12 +183,14 @@ export function scopeScans(scans, closure, { rustUnits = [] } = {}) {
179
183
  const rust = /^(?:clippy|check|check-features|check-dependent|doc|test:rust|test:rust-serial):(.+)$/.exec(s.id);
180
184
  const cargoList = CARGO_PACKAGE_LIST.exec(s.id);
181
185
  let include = true;
182
- if (npm) include = closure.npm.has(npm[1]);
186
+ if (s.internal === "refuse" && s.unit?.npm) include = closure.npm.has(s.unit.npm);
187
+ else if (npm) include = closure.npm.has(npm[1]);
183
188
  else if (cargoList) {
184
189
  if (!names.includes(cargoList[1])) throw problem(`scan ${s.id} has no known Rust owner`);
185
190
  include = closure.rust.has(cargoList[1]);
186
191
  }
187
192
  else if (CARGO_WORKSPACE_SCAN.test(s.id)) include = closureWorkspaces.has(cleanWorkspace(s.cwd));
193
+ else if (/^pnpm-(?:lockfile|audit)(?::.+)?$/.test(s.id)) include = closureNpmRoots.has(cleanWorkspace(s.cwd));
188
194
  else if (/^cargo-/.test(s.id)) throw problem(`executing scan ${s.id} has no exact scope owner`);
189
195
  else if (Array.isArray(s.batch?.units)) {
190
196
  const selected = s.batch.units.filter((n) => closure.rust.has(n));
Binary file