@rightkit/release 0.3.0 → 0.4.1
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/README.md +74 -10
- package/addon-command.mjs +3 -4
- package/adoption-evidence.mjs +650 -0
- package/android-play-upload.mjs +2 -1
- package/apple-profile-create.mjs +113 -10
- package/apple-profile-renew.mjs +11 -3
- package/artifact-storage.mjs +35 -2
- package/bin/darwin/rightkit-swap +0 -0
- package/binary-license-notices.mjs +161 -0
- package/build-release.mjs +138 -47
- package/cargo-license-notices.mjs +111 -0
- package/cli/right-release.mjs +21 -2
- package/codesign-environment.mjs +2 -2
- package/content-digest.mjs +209 -0
- package/development.mjs +17 -6
- package/direct-bootstrap.mjs +5 -6
- package/disk-command.mjs +1 -0
- package/disk-policy.json +6 -2
- package/disk-stable-root.mjs +13 -9
- package/github-updater-release.mjs +70 -7
- package/in-place-install.mjs +17 -14
- package/install-app-windows.mjs +24 -7
- package/install-app.mjs +21 -40
- package/ios-asc-credentials.mjs +163 -0
- package/ios-build-context.mjs +129 -0
- package/ios-build-upload.mjs +282 -0
- package/ios-credentials-command.mjs +70 -0
- package/ios-dev.mjs +248 -0
- package/ios-host.mjs +76 -0
- package/ios-lock.mjs +75 -0
- package/ios-package-recipe.mjs +707 -0
- package/ios-testflight.mjs +266 -0
- package/ios-upload.mjs +313 -0
- package/local-publish.mjs +222 -53
- package/managed-cargo.mjs +8 -0
- package/mirror-root-artifact.mjs +5 -1
- package/native-cargo-layout.mjs +47 -4
- package/ownership-command.mjs +2 -1
- package/ownership-dependency-allowlist.json +3 -1
- package/ownership-evidence.mjs +30 -18
- package/ownership-key-command.mjs +69 -2
- package/ownership-mandatory-set.json +117 -67
- package/ownership-mandatory-set.mjs +141 -21
- package/ownership-repositories.json +1 -1
- package/ownership-scope.mjs +94 -0
- package/ownership.mjs +49 -7
- package/package-recipe-targets.mjs +41 -1
- package/package.json +1 -1
- package/pipeline-admission.mjs +375 -0
- package/pipeline-config.mjs +100 -0
- package/pipeline-generators.json +4 -0
- package/pipeline-generators.mjs +137 -0
- package/pipeline-scan.mjs +285 -0
- package/pipeline-tools.mjs +171 -0
- package/preflight.mjs +14 -7
- package/production-mode.mjs +42 -0
- package/project-type.mjs +3 -1
- package/protected-credentials.mjs +28 -1
- package/publication-artifacts.mjs +30 -5
- package/publication-gate.mjs +94 -32
- package/publish-cargo.mjs +117 -25
- package/publish-swift.mjs +12 -3
- package/publish-update.mjs +15 -2
- package/qa-build-features.mjs +16 -13
- package/qa-contract.mjs +30 -2
- package/qa-control-scan.mjs +8 -0
- package/release-config.mjs +330 -0
- package/release-invocation.mjs +118 -8
- package/release-switch-registry.json +126 -27
- package/release-switch-scan.mjs +88 -21
- package/release.mjs +82 -20
- package/rightkit-ownership.json +1 -1
- package/rightkit-sync.mjs +8 -0
- package/rightkit-versions.json +428 -194
- package/runtime-budget-crates.mjs +14 -0
- package/runtime-budgets.mjs +248 -47
- package/sign-updater.mjs +56 -59
- package/source-gate.mjs +8 -1
- package/stage-sidecar.mjs +207 -40
- package/swap-helper-bundle.mjs +7 -3
- package/target-distribution.mjs +5 -0
- package/upload-release.mjs +7 -2
- package/bin/darwin/rightkit-swap.sign.json +0 -6
package/release.mjs
CHANGED
|
@@ -11,8 +11,8 @@ import { validateRightKitCargoContract } from "./cargo-contract.mjs";
|
|
|
11
11
|
import { assertCargoProfileContract } from "./cargo-profile-contract.mjs";
|
|
12
12
|
import { assertQaAdoption, assertQaBackdoorContract } from "./qa-contract.mjs";
|
|
13
13
|
import { assertLegalReleaseContract } from "./legal-contract.mjs";
|
|
14
|
-
import { assertPrimaryReleaseCheckout, dirtyBuildInputs, resolveReleaseBuildInputs } from "./release-invocation.mjs";
|
|
15
|
-
import { resolveNativeCargoLayout } from "./native-cargo-layout.mjs";
|
|
14
|
+
import { assertNativeIosPrepareHasManifest, assertPrimaryReleaseCheckout, dirtyBuildInputs, resolveReleaseBuildInputs } from "./release-invocation.mjs";
|
|
15
|
+
import { declaredCargoManifest, hasDefaultTauriManifest, resolveNativeCargoLayout } from "./native-cargo-layout.mjs";
|
|
16
16
|
import {
|
|
17
17
|
canonicalPackageIdentity,
|
|
18
18
|
createNativeFinalizationReceipt,
|
|
@@ -48,9 +48,13 @@ import { assertNoQaControl, assertNoWebBundleMocks } from "./qa-control-scan.mjs
|
|
|
48
48
|
import { assertReleaseDependencies } from "./release-dependencies.mjs";
|
|
49
49
|
import { assertNoForbiddenBuildFeatures } from "./qa-build-features.mjs";
|
|
50
50
|
import { expandRecipeBuildCommands } from "./recipe-build.mjs";
|
|
51
|
-
import { assertNoReleaseSwitches, commandTokens, resolveFeatureClosure } from "./release-switch-scan.mjs";
|
|
51
|
+
import { assertNoReleaseSwitches, commandTokens, localCrateDirsFromTree, resolveFeatureClosure } from "./release-switch-scan.mjs";
|
|
52
|
+
import { iosOwnershipScope } from "./ownership-scope.mjs";
|
|
52
53
|
import { devtoolsEvidence, resolveDevtoolsAllowance } from "./devtools-policy.mjs";
|
|
53
54
|
import { runAndroidRelease, validateAndroidTarget } from "./android-release.mjs";
|
|
55
|
+
import { runIosPackageRecipe } from "./ios-package-recipe.mjs";
|
|
56
|
+
import { releaseSwitchInputs } from "./ios-build-context.mjs";
|
|
57
|
+
import { productionBuildEnv, productionModeProblems } from "./production-mode.mjs";
|
|
54
58
|
|
|
55
59
|
const TOOL_ROOT = path.dirname(fileURLToPath(import.meta.url));
|
|
56
60
|
const HARDENING_SCAN = path.resolve(TOOL_ROOT, "hardeningscan.mjs");
|
|
@@ -180,11 +184,14 @@ const SUPERSEDE_JOB_HEAD = /^\n supersede:\n needs: \[private-repo-guard\]\n
|
|
|
180
184
|
|
|
181
185
|
const root = path.dirname(configPath);
|
|
182
186
|
const workdir = path.resolve(root, config.workdir ?? ".");
|
|
183
|
-
|
|
187
|
+
// A native iOS app (xcodegen project; Rust built by iosPackage.prepare) with no src-tauri and no declared nativeAssembly
|
|
188
|
+
// manifest owns no Cargo layout: null, so the Cargo-derived preflight, sweep and package-contract steps are skipped for it.
|
|
189
|
+
const nativeIos = opts.platform === "ios" && !hasDefaultTauriManifest({ appRoot: root });
|
|
190
|
+
const nativeLayout = nativeIos ? null : resolveNativeCargoLayout({ appRoot: root, config });
|
|
184
191
|
const nativeAssembly = config.nativeAssembly;
|
|
185
192
|
const nativeFinalizer = resolveNativeFinalizerConfig(nativeAssembly);
|
|
186
|
-
// A SwiftPM app without a package.json has no Node package contract to validate.
|
|
187
|
-
if (!(resolveProjectType({ appRoot: root, config, target: config.targets?.[opts.platform] }) === "swiftpm" && !existsSync(path.join(root, "package.json")))) {
|
|
193
|
+
// A SwiftPM app or a native iOS app without a package.json has no Node package contract to validate.
|
|
194
|
+
if (!((nativeIos || resolveProjectType({ appRoot: root, config, target: config.targets?.[opts.platform] }) === "swiftpm") && !existsSync(path.join(root, "package.json")))) {
|
|
188
195
|
await validateRightKitPackageContract(root, config.app, config.hostedWorkflows);
|
|
189
196
|
}
|
|
190
197
|
validateRightKitCargoContract(root, RIGHTKIT_CARGO_ALLOWED, config.app ?? path.basename(root));
|
|
@@ -257,12 +264,18 @@ let androidTarget; // validated Android AAB target (android only; see android-re
|
|
|
257
264
|
if (opts.platform === "android") {
|
|
258
265
|
try { androidTarget = validateAndroidTarget(target, { root }); } catch (error) { fail(`${config.app ?? "app"} android ${error.message}`); }
|
|
259
266
|
}
|
|
267
|
+
let iosSpec; // validated iOS iosPackage spec (ios only; package-recipe-targets.mjs expands targets.ios, ios-package-recipe.mjs runs it)
|
|
268
|
+
if (opts.platform === "ios") {
|
|
269
|
+
if (target.recipe?.kind !== "iosPackage") fail(`${config.app ?? "app"} ios must declare targets.ios.iosPackage; there is no hand-written iOS package script`);
|
|
270
|
+
iosSpec = target.recipe.spec;
|
|
271
|
+
if (nativeIos) { try { assertNativeIosPrepareHasManifest(iosSpec, config, root); } catch (error) { fail(`${config.app ?? "app"} ios ${error.message}`); } }
|
|
272
|
+
}
|
|
260
273
|
if (opts.upload && target.publishBlocked) fail(`${config.app ?? "app"} ${opts.platform} publish blocked: ${target.publishBlocked}`);
|
|
261
274
|
/**
|
|
262
275
|
* Assemble preflight inputs from the app's own files. Kept here (not in
|
|
263
276
|
* preflight.mjs) so the check module stays free of release.mjs's config shape.
|
|
264
277
|
*/
|
|
265
|
-
function releasePreflight({ config, configPath, root, repoRoot, platform, nativeLayout: suppliedNativeLayout = nativeLayout, projectTarget = config.targets?.[platform] }) {
|
|
278
|
+
function releasePreflight({ config, configPath, root, repoRoot, platform, nativeLayout: suppliedNativeLayout = nativeLayout, projectTarget = config.targets?.[platform], pinnedCargoLocks = [] }) {
|
|
266
279
|
let version;
|
|
267
280
|
try {
|
|
268
281
|
version = JSON.parse(readFileSync(path.join(root, "package.json"), "utf8")).version;
|
|
@@ -273,6 +286,7 @@ function releasePreflight({ config, configPath, root, repoRoot, platform, native
|
|
|
273
286
|
// them means the build needs a real Windows Perl.
|
|
274
287
|
const cargoLockPaths = [
|
|
275
288
|
...(suppliedNativeLayout?.lockCandidates ?? []),
|
|
289
|
+
...pinnedCargoLocks,
|
|
276
290
|
...(config.cargoLocks ?? []).map((entry) => path.resolve(root, entry)),
|
|
277
291
|
].filter((entry, index, entries) => entries.indexOf(entry) === index);
|
|
278
292
|
return collectPreflight({
|
|
@@ -286,12 +300,14 @@ function releasePreflight({ config, configPath, root, repoRoot, platform, native
|
|
|
286
300
|
nativeLayout: suppliedNativeLayout,
|
|
287
301
|
targetLink: suppliedNativeLayout?.targetLink,
|
|
288
302
|
projectType: resolveProjectType({ appRoot: root, config, target: projectTarget }),
|
|
303
|
+
rust: Boolean(suppliedNativeLayout) || pinnedCargoLocks.length > 0,
|
|
304
|
+
bridge: Boolean(suppliedNativeLayout),
|
|
289
305
|
msix: targetUsesMsix(projectTarget),
|
|
290
306
|
});
|
|
291
307
|
}
|
|
292
308
|
|
|
293
309
|
if (opts.doctor) {
|
|
294
|
-
const { buildInputs, nativeLayout: resolvedNativeLayout } = resolveReleaseBuildInputs({
|
|
310
|
+
const { buildInputs, nativeLayout: resolvedNativeLayout, cargoLockPaths: pinnedCargoLocks } = resolveReleaseBuildInputs({
|
|
295
311
|
repoRoot: doctorInvocation.repoRoot,
|
|
296
312
|
appRoot: root,
|
|
297
313
|
configPath,
|
|
@@ -335,7 +351,7 @@ if (opts.doctor) {
|
|
|
335
351
|
|
|
336
352
|
// Config above, MACHINE below. Printing config never told anyone whether this
|
|
337
353
|
// box could finish a build; these checks do.
|
|
338
|
-
const checks = releasePreflight({ config, configPath, root, repoRoot: doctorInvocation.repoRoot, platform: opts.platform, nativeLayout: resolvedNativeLayout });
|
|
354
|
+
const checks = releasePreflight({ config, configPath, root, repoRoot: doctorInvocation.repoRoot, platform: opts.platform, nativeLayout: resolvedNativeLayout, pinnedCargoLocks });
|
|
339
355
|
console.log("preflight:");
|
|
340
356
|
console.log(formatPreflight(checks));
|
|
341
357
|
const failures = preflightFailures(checks);
|
|
@@ -350,10 +366,15 @@ let prePackScanned = false; // set once a pre-pack payload scan has seen a nativ
|
|
|
350
366
|
// or app Cargo manifest enables devtools, qa-native, qa-control or dev-native (also in --dry-run).
|
|
351
367
|
// DevTools: refused for every app except an allow-listed one that declares `devtools: "content-only"` (devtools-policy.mjs).
|
|
352
368
|
let devtoolsAllowance = { mode: "denied" };
|
|
369
|
+
// The Rust a build compiles is checked from the manifest it compiles: targets.<os>.cargoManifest, else the declared
|
|
370
|
+
// nativeAssembly.cargoManifest. iosPackage.prepare commands run cargo for a native iOS app, so they are scanned like any build command.
|
|
371
|
+
const switchInputs = releaseSwitchInputs({ appRoot: root, config, target, prepare: iosSpec?.prepare ?? [], nativeIos });
|
|
372
|
+
if (switchInputs.problems.length) fail(`${config.app ?? "app"} ios release-switch scan cannot see what is built:\n- ${switchInputs.problems.join("\n- ")}`);
|
|
353
373
|
try {
|
|
354
374
|
devtoolsAllowance = resolveDevtoolsAllowance({ config, root });
|
|
355
375
|
if (devtoolsAllowance.mode === "content-only") console.log(`right-release: devtools allowance recorded: ${JSON.stringify(devtoolsEvidence(devtoolsAllowance))}`);
|
|
356
|
-
assertNoForbiddenBuildFeatures({ root, tier: "release/update", devtools: devtoolsAllowance, cargoManifest:
|
|
376
|
+
assertNoForbiddenBuildFeatures({ root, tier: "release/update", devtools: devtoolsAllowance, cargoManifest: switchInputs.cargoManifest, manifest: config.releaseSwitches?.manifest, commands: [
|
|
377
|
+
...switchInputs.commands,
|
|
357
378
|
{ label: "target.prePackage", command: target.prePackage },
|
|
358
379
|
{ label: "target.package", command: target.package },
|
|
359
380
|
{ label: "nativeAssembly.packageHook", command: nativeAssembly?.packageHook },
|
|
@@ -362,6 +383,13 @@ try {
|
|
|
362
383
|
{ label: "target.gradle", command: target.gradle },
|
|
363
384
|
] });
|
|
364
385
|
} catch (error) { fail(error.message); }
|
|
386
|
+
// M12: the frontend build resolves in production mode only (development-only conditional exports such as
|
|
387
|
+
// `@rightkit/license/testing` never resolve). `run` pins NODE_ENV=production for build children; this refuses a
|
|
388
|
+
// Vite config that lists the `development` resolve condition explicitly, which the pin cannot override.
|
|
389
|
+
{
|
|
390
|
+
const modeProblems = productionModeProblems({ appRoot: root });
|
|
391
|
+
if (modeProblems.length) fail(`${config.app ?? "app"} release frontend is not production-only:\n- ${modeProblems.join("\n- ")}`);
|
|
392
|
+
}
|
|
365
393
|
|
|
366
394
|
// D4 (M21/M23) source layer: every environment/argument read in app source is registered in the app's switches.json and
|
|
367
395
|
// every S1-S4 selector is compiled out of release. The inventory is `config.releaseSwitches.inventory` (relative to the
|
|
@@ -374,17 +402,18 @@ const releaseSwitchInventoryText = existsSync(releaseSwitchInventoryPath) ? read
|
|
|
374
402
|
try {
|
|
375
403
|
// A `node recipe-build.mjs --plan <JSON>` prePackage hides each step's `--features` in one argument: the closure and the
|
|
376
404
|
// command token scan see every prepare / build / postBuild step (a plan that cannot be read or validated fails closed).
|
|
377
|
-
const closureCommands = [target.prePackage, target.package, nativeAssembly?.packageHook, nativeFinalizer, ...commandList(target.postSign), target.gradle].filter(Boolean).flatMap((c) => expandRecipeBuildCommands(c, { root }));
|
|
405
|
+
const closureCommands = [target.prePackage, target.package, nativeAssembly?.packageHook, nativeFinalizer, ...commandList(target.postSign), target.gradle, ...switchInputs.cargoCommands].filter(Boolean).flatMap((c) => expandRecipeBuildCommands(c, { root }));
|
|
378
406
|
assertReleaseDependencies({ root, config, target, commands: closureCommands });
|
|
379
407
|
let treeText;
|
|
380
408
|
try {
|
|
381
409
|
const closure = resolveFeatureClosure({
|
|
382
410
|
root,
|
|
383
411
|
manifest: config.releaseSwitches?.manifest,
|
|
384
|
-
cargoManifest:
|
|
412
|
+
cargoManifest: switchInputs.cargoManifest,
|
|
385
413
|
rust: config.releaseSwitches?.rust,
|
|
386
414
|
features: config.releaseSwitches?.features ?? [],
|
|
387
|
-
triple
|
|
415
|
+
// iOS ships one device triple; "all" is refused by the build broker and would pull in desktop-only targets.
|
|
416
|
+
triple: target.cargoTarget ?? target.targetTriple ?? target.rustTarget ?? (opts.platform === "ios" ? "aarch64-apple-ios" : undefined),
|
|
388
417
|
commandTexts: closureCommands,
|
|
389
418
|
});
|
|
390
419
|
if (closure.skipped) console.log(`right-release: feature closure skipped (${closure.skipped})`);
|
|
@@ -394,9 +423,27 @@ try {
|
|
|
394
423
|
console.log(`right-release: dry run: ${error.message}`);
|
|
395
424
|
}
|
|
396
425
|
// remoteHost: rightkit-daemon `remote` is refused in the closure unless the app declares releaseSwitches.remoteHost: true.
|
|
397
|
-
|
|
426
|
+
// remoteLoopback: the daemon `remote-loopback` feature is a separate declaration (releaseSwitches.remoteLoopback: true); remoteHost does not cover it.
|
|
427
|
+
const closureInput = { treeText, devtools: devtoolsAllowance, remoteHost: config.releaseSwitches?.remoteHost, remoteLoopback: config.releaseSwitches?.remoteLoopback, commands: closureCommands.map((c) => commandTokens(c)) };
|
|
398
428
|
if (releaseSwitchInventoryText != null) {
|
|
399
|
-
|
|
429
|
+
// iOS: only what the iPhone app compiles — the Xcode project and its local packages, plus the local crates in the
|
|
430
|
+
// resolved aarch64-apple-ios cargo graph (feature-aware, unlike the ownership closure). Other targets walk the app root.
|
|
431
|
+
let sourceRoots;
|
|
432
|
+
if (opts.platform === "ios") {
|
|
433
|
+
const repoTop = git(root, ["rev-parse", "--show-toplevel"]);
|
|
434
|
+
// Swift side only: the Rust side is the resolved cargo graph below. The ownership scope also folds in the whole
|
|
435
|
+
// nativeAssembly workspace, which would scan crates (desktop tools, the TUI) the iPhone never links.
|
|
436
|
+
const swiftSide = (iosOwnershipScope({ repoRoot: repoTop, appRoot: root, target: config.targets?.ios }) ?? []).filter((dir) => dir !== ".rightkit").map((dir) => path.join(repoTop, dir));
|
|
437
|
+
sourceRoots = [...new Set([...swiftSide, ...localCrateDirsFromTree(treeText, repoTop)])].sort();
|
|
438
|
+
// A prepare step can stage sources from anywhere (a web bundle, a generated resource) into the app; its working directory must be
|
|
439
|
+
// inside the scanned roots, or the release fails instead of shipping what the scan never saw. Add the directory to ownershipScope.
|
|
440
|
+
for (const step of config.targets?.ios?.iosPackage?.prepare ?? []) {
|
|
441
|
+
const cwd = path.resolve(root, step?.cwd ?? ".");
|
|
442
|
+
if (!sourceRoots.some((dir) => cwd === dir || cwd.startsWith(dir + path.sep))) throw new Error(`source switch inventory: targets.ios.iosPackage.prepare runs in ${path.relative(repoTop, cwd) || "."}, outside the iOS scan roots (${sourceRoots.map((dir) => path.relative(repoTop, dir) || ".").join(", ")}); list that directory in targets.ios.ownershipScope`);
|
|
443
|
+
}
|
|
444
|
+
console.log(`right-release: source switch scan (ios): ${sourceRoots.map((dir) => path.relative(repoTop, dir) || ".").join(", ")}`);
|
|
445
|
+
}
|
|
446
|
+
assertNoReleaseSwitches({ ...closureInput, sourceRoot: root, sourceRoots, inventoryText: releaseSwitchInventoryText, appForbid: config.releaseSwitches?.forbid ?? [] }, "source switch inventory");
|
|
400
447
|
} else if (treeText != null || closureInput.commands.length) {
|
|
401
448
|
assertNoReleaseSwitches({ ...closureInput, appForbid: config.releaseSwitches?.forbid ?? [] }, "feature closure");
|
|
402
449
|
}
|
|
@@ -408,7 +455,7 @@ try {
|
|
|
408
455
|
const releaseLock = acquireReleaseLock(root, config.app ?? path.basename(root), opts.platform, opts.tier);
|
|
409
456
|
const mode = "package";
|
|
410
457
|
const command = target.package;
|
|
411
|
-
if (!command && opts.platform !== "android" && !packageCommandOptional(target, resolveProjectType({ appRoot: root, config, target }))) fail(`${config.app ?? "app"} has no ${opts.platform} ${mode} command`);
|
|
458
|
+
if (!command && opts.platform !== "android" && opts.platform !== "ios" && !packageCommandOptional(target, resolveProjectType({ appRoot: root, config, target }))) fail(`${config.app ?? "app"} has no ${opts.platform} ${mode} command`);
|
|
412
459
|
if (opts.upload && !target.publish && !target.upload) {
|
|
413
460
|
fail(`${config.app ?? "app"} has no ${opts.platform} upload config`);
|
|
414
461
|
}
|
|
@@ -524,6 +571,16 @@ if (opts.platform === "win" && target.signingContract === MSIX_SIGNING_CONTRACT)
|
|
|
524
571
|
receiptDir: path.resolve(process.env.RIGHT_RELEASE_STATE_ROOT ?? path.join(root, ".right-release", "receipts")),
|
|
525
572
|
allowContentOnlyDevtools: devtoolsAllowance.mode === "content-only",
|
|
526
573
|
});
|
|
574
|
+
} else if (opts.platform === "ios") {
|
|
575
|
+
// Archive and export with Xcode automatic signing in the GUI session, then the IPA verification and the receipt (macOS only;
|
|
576
|
+
// --dry-run prints the plan anywhere). Upload is a separate command: `right-release upload --platform ios`.
|
|
577
|
+
await runIosPackageRecipe({
|
|
578
|
+
root, app: config.app ?? path.basename(root), spec: iosSpec, dryRun: opts.dryRun,
|
|
579
|
+
receiptDir: path.resolve(process.env.RIGHT_RELEASE_STATE_ROOT ?? path.join(root, ".right-release", "receipts")),
|
|
580
|
+
scanFiles: (label, files, scanOptions) => assertNoQaControlBinaries(label, files, scanOptions),
|
|
581
|
+
// The build number counter outlives every release id: it lives in the vault state root that right-release build exports.
|
|
582
|
+
...(process.env.RIGHT_RELEASE_VAULT_STATE_ROOT ? { counterDir: path.join(path.resolve(process.env.RIGHT_RELEASE_VAULT_STATE_ROOT), "ios-build-numbers") } : {}),
|
|
583
|
+
});
|
|
527
584
|
} else if (nativeAssembly) {
|
|
528
585
|
if (nativeAssembly.packageHook) await runCommand(nativeAssembly.packageHook, root);
|
|
529
586
|
if (nativeFinalizer) {
|
|
@@ -647,7 +704,7 @@ function assertNoQaControlBinaries(label, files, { requireBinary = false, prePac
|
|
|
647
704
|
try {
|
|
648
705
|
const stats = assertNoQaControl(files, label, { requireBinary, allowContentOnlyDevtools: devtoolsAllowance.mode === "content-only" });
|
|
649
706
|
// D4 binary layer, same inputs: no test-mode marker and no S1-S4 selector name in any native binary.
|
|
650
|
-
assertNoReleaseSwitches({ binaryPaths: files, inventoryText: releaseSwitchInventoryText ?? undefined, appForbid: config.releaseSwitches?.forbid ?? [], requireBinary: false }, `${label} (release switches)`);
|
|
707
|
+
assertNoReleaseSwitches({ binaryPaths: files, devtools: devtoolsAllowance, inventoryText: releaseSwitchInventoryText ?? undefined, appForbid: config.releaseSwitches?.forbid ?? [], requireBinary: false }, `${label} (release switches)`);
|
|
651
708
|
if (prePack && stats.binaries > 0) prePackScanned = true;
|
|
652
709
|
for (const opaque of stats.opaque) {
|
|
653
710
|
console.log(`right-release: ${label}: ${opaque.kind.toUpperCase()} payload is compressed and cannot be scanned (${opaque.file}); the raw payload was scanned before packing`);
|
|
@@ -908,6 +965,8 @@ function hasNativeTargetSpecificPrePackage(target, packageHook) {
|
|
|
908
965
|
// publish so it cannot race an artifact, and is best-effort: a missing
|
|
909
966
|
// cargo-sweep or a sweep failure must never fail a release.
|
|
910
967
|
function sweepStaleRustArtifacts() {
|
|
968
|
+
// No Cargo host, nothing of ours to sweep.
|
|
969
|
+
if (!nativeLayout) return;
|
|
911
970
|
const candidates = [...new Set([root, workdir, nativeLayout.manifestDir, nativeLayout.workspaceRoot])];
|
|
912
971
|
const projects = candidates.filter((dir) => existsSync(path.join(dir, "target")));
|
|
913
972
|
for (const dir of projects) {
|
|
@@ -929,8 +988,8 @@ function sweepStaleRustArtifacts() {
|
|
|
929
988
|
}
|
|
930
989
|
|
|
931
990
|
async function runInstall(pm, cwd) {
|
|
932
|
-
if (pm === "pnpm") await run("pnpm", ["install", "--frozen-lockfile"], cwd);
|
|
933
|
-
else if (pm === "npm") await run("npm", ["ci"], cwd);
|
|
991
|
+
if (pm === "pnpm") await run("pnpm", ["install", "--frozen-lockfile"], cwd, {}, { install: true });
|
|
992
|
+
else if (pm === "npm") await run("npm", ["ci"], cwd, {}, { install: true });
|
|
934
993
|
else fail(`unsupported package manager: ${pm}`);
|
|
935
994
|
}
|
|
936
995
|
|
|
@@ -1432,6 +1491,8 @@ function expandEnv(value) {
|
|
|
1432
1491
|
// notices and the source offer, optionally run the app's stage command, and (before signing) check the staged files.
|
|
1433
1492
|
async function prepareMediaSidecar(root, { stage = true } = {}) {
|
|
1434
1493
|
if (!mediaSidecarConfig) return null;
|
|
1494
|
+
// Unreachable for iOS (mediaSidecar is mac and win only, refused above); a Cargo-less layout must still never be dereferenced.
|
|
1495
|
+
if (!nativeLayout) fail(`${config.app ?? "app"} ${opts.platform} mediaSidecar needs a native Cargo manifest to read the sidecar catalog, and this app has none`);
|
|
1435
1496
|
let plan;
|
|
1436
1497
|
try {
|
|
1437
1498
|
const loaded = loadSidecarCatalog({ config: mediaSidecarConfig, root, manifestPath: nativeLayout.manifestPath });
|
|
@@ -1720,7 +1781,8 @@ function run(cmd, runArgs, cwd, env = {}, options = {}) {
|
|
|
1720
1781
|
const useShell = process.platform === "win32" && !/\.(?:exe|com)$/i.test(cmd);
|
|
1721
1782
|
const child = spawn(cmd, runArgs, {
|
|
1722
1783
|
cwd,
|
|
1723
|
-
|
|
1784
|
+
// Build children resolve in production mode; package installs keep the caller's NODE_ENV (production drops devDependencies).
|
|
1785
|
+
env: macosCodesignEnvironment(options.install ? { ...process.env, ...releaseEnv, ...env } : productionBuildEnv({ ...process.env, ...releaseEnv, ...env })),
|
|
1724
1786
|
stdio: options.captureStdout ? ["inherit", "pipe", "inherit"] : "inherit",
|
|
1725
1787
|
shell: useShell,
|
|
1726
1788
|
windowsHide: true,
|
package/rightkit-ownership.json
CHANGED
|
@@ -2979,7 +2979,7 @@
|
|
|
2979
2979
|
{
|
|
2980
2980
|
"package": "rightkit-spend",
|
|
2981
2981
|
"capability": "spend-ledger",
|
|
2982
|
-
"summary": "Spend reservation, allowance and compare-and-swap ledger on SQLite: integer micro-units, idempotent reserve on request id, in-flight and overrun handling, opaque allowance periods, optional account key and per-reservation currency, billed-unknown commit, caller-transaction (`_in`) variants, strict transition mode",
|
|
2982
|
+
"summary": "Spend reservation, allowance and compare-and-swap ledger on SQLite: integer micro-units, idempotent reserve on request id, in-flight and overrun handling, opaque allowance periods, optional account key and per-reservation currency, billed-unknown commit, caller-transaction (`_in`/`_on`) variants, strict transition mode, carry-over, atomic multi-cap reserve with epoch-reset device allowance, caller and process budgets, provider-reported cost and invoice reconciliation, cap-setting audit, optional overrun gate",
|
|
2983
2983
|
"fingerprints": [],
|
|
2984
2984
|
"detection": "declaration-only",
|
|
2985
2985
|
"policyNotes": [
|
package/rightkit-sync.mjs
CHANGED
|
@@ -192,6 +192,14 @@ export function stageSourceVersions(entries, { file = VERSIONS_FILE, root = righ
|
|
|
192
192
|
return { changes, copies, written };
|
|
193
193
|
}
|
|
194
194
|
|
|
195
|
+
/** Feed file and consumer-copy root of one requested workspace. recordPublished/stageSourceVersions default to the
|
|
196
|
+
* location of THIS module, which is inside the package store when right-release runs from an installed
|
|
197
|
+
* @rightkit/release; every caller that knows its workspace passes these explicitly. */
|
|
198
|
+
export function workspaceFeedOptions(workspaceRoot) {
|
|
199
|
+
const root = path.resolve(workspaceRoot);
|
|
200
|
+
return { root, file: path.join(root, "packages", "release", "rightkit-versions.json") };
|
|
201
|
+
}
|
|
202
|
+
|
|
195
203
|
/** Record one just-published version (the registry index can lag the upload) and refresh the copies.
|
|
196
204
|
* Called by right-release after a verified cargo or npm publish; never fails the publish itself unless `strict` is set. */
|
|
197
205
|
export function recordPublished({ registry, name, version }, { file = VERSIONS_FILE, root = rightkitRoot, log = (line) => process.stdout.write(`${line}\n`), strict = false } = {}) {
|