@rightkit/release 0.6.5 → 0.6.7

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 (73) hide show
  1. package/README.md +196 -11
  2. package/admission-files.mjs +3 -2
  3. package/advisory-db.json +4 -0
  4. package/advisory-db.mjs +124 -0
  5. package/advisory-exceptions.mjs +13 -10
  6. package/android-play-upload.mjs +4 -4
  7. package/app-check.mjs +702 -26
  8. package/apple-profile-create.mjs +4 -3
  9. package/apple-profile-renew.mjs +5 -4
  10. package/bin/darwin/rightkit-swap +0 -0
  11. package/bin/rightkit-agent-hook.mjs +9 -0
  12. package/bin/rightkit-gate.mjs +671 -133
  13. package/binary-license-notices.mjs +2 -1
  14. package/build-release.mjs +20 -12
  15. package/cache-command.mjs +4 -3
  16. package/cargo-license-notices.mjs +2 -1
  17. package/cargo-manifest-text.mjs +25 -0
  18. package/cargo-profile-contract.mjs +26 -13
  19. package/cli/right-release.mjs +37 -7
  20. package/command-spec.mjs +4 -5
  21. package/compile-inputs.mjs +66 -0
  22. package/consumer-contracts.json +1044 -0
  23. package/consumer-contracts.mjs +627 -0
  24. package/consumer-input-contracts.mjs +422 -0
  25. package/consumer-policy.mjs +588 -0
  26. package/consumer-source-contracts.mjs +469 -0
  27. package/deny-default.toml +42 -0
  28. package/deps.mjs +5 -4
  29. package/deterministic-scans.mjs +513 -54
  30. package/development.mjs +5 -5
  31. package/disk-command.mjs +270 -30
  32. package/disk-policy.json +50 -10
  33. package/disk-policy.mjs +482 -35
  34. package/disk-report.mjs +129 -0
  35. package/door-policy.mjs +228 -0
  36. package/gate-executor.mjs +30 -0
  37. package/gate-stages.mjs +110 -0
  38. package/github-release.mjs +5 -3
  39. package/github-updater-release.mjs +48 -8
  40. package/github-upload-gate.mjs +6 -6
  41. package/init-command.mjs +175 -8
  42. package/ios-upload.mjs +4 -4
  43. package/legion-semantic-contracts.mjs +428 -0
  44. package/mirror-root-artifact.mjs +3 -4
  45. package/npm-license-notices.mjs +2 -1
  46. package/outdated-command.mjs +91 -0
  47. package/ownership-class-rules.mjs +277 -0
  48. package/ownership-key-authority.mjs +3 -1
  49. package/ownership-mandatory-set.mjs +3 -2
  50. package/package.json +46 -3
  51. package/pipeline-admission.mjs +12 -0
  52. package/publish-cargo.mjs +219 -4
  53. package/publish-update.mjs +19 -14
  54. package/release-config.mjs +87 -11
  55. package/release-dependencies-command.mjs +4 -3
  56. package/release-qa-audit.mjs +12 -10
  57. package/release-qa-command.mjs +42 -7
  58. package/release-qa-hardening.mjs +10 -3
  59. package/release-qa-policy.mjs +214 -47
  60. package/release.mjs +4 -4
  61. package/rightkit-registry-resolver.mjs +5 -2
  62. package/rightkit-versions.json +178 -170
  63. package/shell-contract-bridge.json +251 -0
  64. package/shell-contract.mjs +1069 -0
  65. package/sign-updater.mjs +24 -4
  66. package/source-closure-rules.mjs +204 -0
  67. package/stage-sidecar-command.mjs +7 -6
  68. package/stage-sidecar.mjs +437 -114
  69. package/suite-api.generated.mjs +96 -6
  70. package/suite-trust-roots.mjs +198 -0
  71. package/test-command.mjs +3 -2
  72. package/upload-release.mjs +3 -2
  73. package/web-build.mjs +2 -1
@@ -108,6 +108,9 @@ export function parseToml(text) {
108
108
  if (raw === 'true') return true;
109
109
  if (raw === 'false') return false;
110
110
  if (/^[+-]?\d[\d_]*(\.\d+)?$/.test(raw)) return Number(raw.replace(/_/g, ''));
111
+ if (/^0x[0-9a-fA-F_]+$/.test(raw)) return parseInt(raw.slice(2).replace(/_/g, ''), 16);
112
+ if (/^0o[0-7_]+$/.test(raw)) return parseInt(raw.slice(2).replace(/_/g, ''), 8);
113
+ if (/^0b[01_]+$/.test(raw)) return parseInt(raw.slice(2).replace(/_/g, ''), 2);
111
114
  return fail(`unsupported value ${raw}`);
112
115
  };
113
116
  for (;;) {
@@ -224,14 +227,14 @@ function dateProblem(entryLabel, value, now) {
224
227
  return null;
225
228
  }
226
229
 
227
- export function checkAdvisoryExceptions({ root = process.cwd(), now = new Date(), readFile, allowPending = false } = {}) {
230
+ export function checkAdvisoryExceptions({ root = process.cwd(), now = new Date(), readFile, allowPending = false, exceptionsFile = EXCEPTIONS_FILE } = {}) {
228
231
  const read = readFile ?? (p => fs.readFileSync(path.join(root, p), 'utf8'));
229
232
  const problems = [];
230
233
  const load = (file, parse) => {
231
234
  try { return parse(read(file)); } catch (err) { problems.push(`${file}: cannot read or parse (${err.message})`); return null; }
232
235
  };
233
236
 
234
- const doc = load(EXCEPTIONS_FILE, JSON.parse);
237
+ const doc = load(exceptionsFile, JSON.parse);
235
238
  const deny = load(DENY_FILE, parseToml);
236
239
  const audit = load(AUDIT_FILE, parseToml);
237
240
  const lockPair = load(LOCK_FILE, t => ({ versions: lockedVersions(t), ...lockedGraph(t) }));
@@ -240,9 +243,9 @@ export function checkAdvisoryExceptions({ root = process.cwd(), now = new Date()
240
243
  const { graph } = lockPair;
241
244
  for (const u of lockPair.unresolved) problems.push(`${LOCK_FILE}: dependency ${u} does not resolve to exactly one locked package`);
242
245
 
243
- if (doc.schema !== 1) problems.push(`${EXCEPTIONS_FILE}: schema must be 1`);
246
+ if (doc.schema !== 1) problems.push(`${exceptionsFile}: schema must be 1`);
244
247
  if (!Array.isArray(doc.exceptions)) {
245
- problems.push(`${EXCEPTIONS_FILE}: exceptions must be an array`);
248
+ problems.push(`${exceptionsFile}: exceptions must be an array`);
246
249
  return problems;
247
250
  }
248
251
 
@@ -250,7 +253,7 @@ export function checkAdvisoryExceptions({ root = process.cwd(), now = new Date()
250
253
  const byId = new Map();
251
254
  const valid = [];
252
255
  doc.exceptions.forEach((e, idx) => {
253
- const label = `${EXCEPTIONS_FILE} entry ${e?.id ?? `#${idx}`}`;
256
+ const label = `${exceptionsFile} entry ${e?.id ?? `#${idx}`}`;
254
257
  if (!e || typeof e !== 'object' || Array.isArray(e)) { problems.push(`${label}: not an object`); return; }
255
258
  let ok = true;
256
259
  for (const f of REQUIRED) {
@@ -329,10 +332,10 @@ export function checkAdvisoryExceptions({ root = process.cwd(), now = new Date()
329
332
  if (seen.has(id)) problems.push(`${DENY_FILE}: duplicate ignore ${id}`);
330
333
  seen.add(id);
331
334
  const entry = byId.get(id);
332
- if (!entry) { problems.push(`${DENY_FILE}: ignore ${id} has no entry in ${EXCEPTIONS_FILE}`); continue; }
333
- if (reason !== entry.rationale) problems.push(`${DENY_FILE}: ignore ${id} reason differs from the rationale in ${EXCEPTIONS_FILE}`);
335
+ if (!entry) { problems.push(`${DENY_FILE}: ignore ${id} has no entry in ${exceptionsFile}`); continue; }
336
+ if (reason !== entry.rationale) problems.push(`${DENY_FILE}: ignore ${id} reason differs from the rationale in ${exceptionsFile}`);
334
337
  }
335
- for (const id of byId.keys()) if (!seen.has(id)) problems.push(`${EXCEPTIONS_FILE}: entry ${id} has no ignore in ${DENY_FILE}`);
338
+ for (const id of byId.keys()) if (!seen.has(id)) problems.push(`${exceptionsFile}: entry ${id} has no ignore in ${DENY_FILE}`);
336
339
  }
337
340
  if (adv) {
338
341
  if (adv.yanked !== 'deny') problems.push(`${DENY_FILE}: [advisories] yanked must be "deny"`);
@@ -348,9 +351,9 @@ export function checkAdvisoryExceptions({ root = process.cwd(), now = new Date()
348
351
  for (const id of auditIgnore) {
349
352
  if (set.has(id)) problems.push(`${AUDIT_FILE}: duplicate ignore ${id}`);
350
353
  set.add(id);
351
- if (!byId.has(id)) problems.push(`${AUDIT_FILE}: ignore ${id} has no entry in ${EXCEPTIONS_FILE}`);
354
+ if (!byId.has(id)) problems.push(`${AUDIT_FILE}: ignore ${id} has no entry in ${exceptionsFile}`);
352
355
  }
353
- for (const id of byId.keys()) if (!set.has(id)) problems.push(`${AUDIT_FILE}: entry ${id} from ${EXCEPTIONS_FILE} is not ignored here (sets differ)`);
356
+ for (const id of byId.keys()) if (!set.has(id)) problems.push(`${AUDIT_FILE}: entry ${id} from ${exceptionsFile} is not ignored here (sets differ)`);
354
357
  }
355
358
 
356
359
  return problems;
@@ -23,8 +23,8 @@
23
23
  import { createSign } from "node:crypto";
24
24
  import { existsSync, mkdirSync, writeFileSync } from "node:fs";
25
25
  import path from "node:path";
26
- import { pathToFileURL } from "node:url";
27
26
  import { spawnSync } from "./background-process.mjs";
27
+ import { defaultReleaseConfigFile, importReleaseConfig } from "./release-config.mjs";
28
28
  import { describeAabProblems, scanAabBuffer } from "./android-aab-scan.mjs";
29
29
  import { sealedManifestAllowsContentDevtools } from "./devtools-policy.mjs";
30
30
  import { targetDistribution } from "./target-distribution.mjs";
@@ -190,7 +190,7 @@ const git = (cwd, args) => {
190
190
  };
191
191
 
192
192
  // Local checks only; no network, no secret. The sealed release is the only input: build-free by construction.
193
- export async function authorizePlayUpload({ releaseId, configName = "right-release.config.mjs", cwd = process.cwd() }) {
193
+ export async function authorizePlayUpload({ releaseId, cwd = process.cwd(), configName = defaultReleaseConfigFile(cwd) }) {
194
194
  if (!RELEASE_ID.test(releaseId ?? "")) refuse("PLAY_RELEASE_REQUIRED", "--release <sealed-id> is required");
195
195
  const repoRoot = git(cwd, ["rev-parse", "--show-toplevel"]);
196
196
  assertPrimaryReleaseCheckout(repoRoot);
@@ -198,8 +198,8 @@ export async function authorizePlayUpload({ releaseId, configName = "right-relea
198
198
  assertCleanSource({ status: git(repoRoot, ["status", "--porcelain=v1", "-z", "--untracked-files=all"]), commandId: "right-release upload" });
199
199
  const configPath = path.resolve(cwd, configName);
200
200
  if (!existsSync(configPath)) refuse("PLAY_CONFIG_INVALID", `release config is required: ${configName}`);
201
- // The config is a module that runs on import: loaded only after the tree is proven clean.
202
- const config = (await import(`${pathToFileURL(configPath).href}?upload=${Date.now()}`)).default;
201
+ // A legacy config is a module that runs on import (a JSON config is data): loaded only after the tree is proven clean.
202
+ const config = await importReleaseConfig(configPath, { query: `upload=${Date.now()}` });
203
203
  const distribution = validatePlayDistribution(targetDistribution(config, "android"));
204
204
  const sealedDir = path.join(repoRoot, ".right-release", "sealed", releaseId, "android");
205
205
  const sealed = verifyPublicationRelease({ sealedDir, repoRoot, appRoot: path.dirname(configPath), commit, app: config.app, version: config.version, platform: "android", configPath, releaseId });