@rightkit/release 0.3.0 → 0.4.0
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 +25 -4
- package/addon-command.mjs +3 -4
- package/android-play-upload.mjs +2 -1
- package/apple-profile-renew.mjs +11 -3
- package/artifact-storage.mjs +35 -2
- package/bin/darwin/rightkit-swap +0 -0
- package/bin/darwin/rightkit-swap.sign.json +1 -1
- package/build-release.mjs +138 -47
- package/cli/right-release.mjs +11 -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 +92 -37
- 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 +1 -1
- package/ownership-evidence.mjs +30 -18
- package/ownership-key-command.mjs +69 -2
- package/ownership-mandatory-set.mjs +66 -16
- 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/preflight.mjs +14 -7
- package/project-type.mjs +3 -1
- package/protected-credentials.mjs +28 -1
- package/publication-artifacts.mjs +30 -5
- package/publication-gate.mjs +15 -4
- package/publish-cargo.mjs +109 -22
- package/publish-update.mjs +15 -2
- package/qa-build-features.mjs +16 -13
- package/qa-contract.mjs +30 -2
- package/qa-control-scan.mjs +4 -0
- package/release-invocation.mjs +118 -8
- package/release-switch-registry.json +126 -27
- package/release-switch-scan.mjs +45 -17
- package/release.mjs +69 -17
- package/rightkit-ownership.json +1 -1
- package/rightkit-versions.json +379 -189
- package/runtime-budgets.mjs +120 -16
- package/sign-updater.mjs +56 -59
- package/source-gate.mjs +8 -1
- package/stage-sidecar.mjs +160 -32
- package/target-distribution.mjs +5 -0
- package/upload-release.mjs +7 -2
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,12 @@ 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";
|
|
54
57
|
|
|
55
58
|
const TOOL_ROOT = path.dirname(fileURLToPath(import.meta.url));
|
|
56
59
|
const HARDENING_SCAN = path.resolve(TOOL_ROOT, "hardeningscan.mjs");
|
|
@@ -180,11 +183,14 @@ const SUPERSEDE_JOB_HEAD = /^\n supersede:\n needs: \[private-repo-guard\]\n
|
|
|
180
183
|
|
|
181
184
|
const root = path.dirname(configPath);
|
|
182
185
|
const workdir = path.resolve(root, config.workdir ?? ".");
|
|
183
|
-
|
|
186
|
+
// A native iOS app (xcodegen project; Rust built by iosPackage.prepare) with no src-tauri and no declared nativeAssembly
|
|
187
|
+
// manifest owns no Cargo layout: null, so the Cargo-derived preflight, sweep and package-contract steps are skipped for it.
|
|
188
|
+
const nativeIos = opts.platform === "ios" && !hasDefaultTauriManifest({ appRoot: root });
|
|
189
|
+
const nativeLayout = nativeIos ? null : resolveNativeCargoLayout({ appRoot: root, config });
|
|
184
190
|
const nativeAssembly = config.nativeAssembly;
|
|
185
191
|
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")))) {
|
|
192
|
+
// A SwiftPM app or a native iOS app without a package.json has no Node package contract to validate.
|
|
193
|
+
if (!((nativeIos || resolveProjectType({ appRoot: root, config, target: config.targets?.[opts.platform] }) === "swiftpm") && !existsSync(path.join(root, "package.json")))) {
|
|
188
194
|
await validateRightKitPackageContract(root, config.app, config.hostedWorkflows);
|
|
189
195
|
}
|
|
190
196
|
validateRightKitCargoContract(root, RIGHTKIT_CARGO_ALLOWED, config.app ?? path.basename(root));
|
|
@@ -257,12 +263,18 @@ let androidTarget; // validated Android AAB target (android only; see android-re
|
|
|
257
263
|
if (opts.platform === "android") {
|
|
258
264
|
try { androidTarget = validateAndroidTarget(target, { root }); } catch (error) { fail(`${config.app ?? "app"} android ${error.message}`); }
|
|
259
265
|
}
|
|
266
|
+
let iosSpec; // validated iOS iosPackage spec (ios only; package-recipe-targets.mjs expands targets.ios, ios-package-recipe.mjs runs it)
|
|
267
|
+
if (opts.platform === "ios") {
|
|
268
|
+
if (target.recipe?.kind !== "iosPackage") fail(`${config.app ?? "app"} ios must declare targets.ios.iosPackage; there is no hand-written iOS package script`);
|
|
269
|
+
iosSpec = target.recipe.spec;
|
|
270
|
+
if (nativeIos) { try { assertNativeIosPrepareHasManifest(iosSpec, config, root); } catch (error) { fail(`${config.app ?? "app"} ios ${error.message}`); } }
|
|
271
|
+
}
|
|
260
272
|
if (opts.upload && target.publishBlocked) fail(`${config.app ?? "app"} ${opts.platform} publish blocked: ${target.publishBlocked}`);
|
|
261
273
|
/**
|
|
262
274
|
* Assemble preflight inputs from the app's own files. Kept here (not in
|
|
263
275
|
* preflight.mjs) so the check module stays free of release.mjs's config shape.
|
|
264
276
|
*/
|
|
265
|
-
function releasePreflight({ config, configPath, root, repoRoot, platform, nativeLayout: suppliedNativeLayout = nativeLayout, projectTarget = config.targets?.[platform] }) {
|
|
277
|
+
function releasePreflight({ config, configPath, root, repoRoot, platform, nativeLayout: suppliedNativeLayout = nativeLayout, projectTarget = config.targets?.[platform], pinnedCargoLocks = [] }) {
|
|
266
278
|
let version;
|
|
267
279
|
try {
|
|
268
280
|
version = JSON.parse(readFileSync(path.join(root, "package.json"), "utf8")).version;
|
|
@@ -273,6 +285,7 @@ function releasePreflight({ config, configPath, root, repoRoot, platform, native
|
|
|
273
285
|
// them means the build needs a real Windows Perl.
|
|
274
286
|
const cargoLockPaths = [
|
|
275
287
|
...(suppliedNativeLayout?.lockCandidates ?? []),
|
|
288
|
+
...pinnedCargoLocks,
|
|
276
289
|
...(config.cargoLocks ?? []).map((entry) => path.resolve(root, entry)),
|
|
277
290
|
].filter((entry, index, entries) => entries.indexOf(entry) === index);
|
|
278
291
|
return collectPreflight({
|
|
@@ -286,12 +299,14 @@ function releasePreflight({ config, configPath, root, repoRoot, platform, native
|
|
|
286
299
|
nativeLayout: suppliedNativeLayout,
|
|
287
300
|
targetLink: suppliedNativeLayout?.targetLink,
|
|
288
301
|
projectType: resolveProjectType({ appRoot: root, config, target: projectTarget }),
|
|
302
|
+
rust: Boolean(suppliedNativeLayout) || pinnedCargoLocks.length > 0,
|
|
303
|
+
bridge: Boolean(suppliedNativeLayout),
|
|
289
304
|
msix: targetUsesMsix(projectTarget),
|
|
290
305
|
});
|
|
291
306
|
}
|
|
292
307
|
|
|
293
308
|
if (opts.doctor) {
|
|
294
|
-
const { buildInputs, nativeLayout: resolvedNativeLayout } = resolveReleaseBuildInputs({
|
|
309
|
+
const { buildInputs, nativeLayout: resolvedNativeLayout, cargoLockPaths: pinnedCargoLocks } = resolveReleaseBuildInputs({
|
|
295
310
|
repoRoot: doctorInvocation.repoRoot,
|
|
296
311
|
appRoot: root,
|
|
297
312
|
configPath,
|
|
@@ -335,7 +350,7 @@ if (opts.doctor) {
|
|
|
335
350
|
|
|
336
351
|
// Config above, MACHINE below. Printing config never told anyone whether this
|
|
337
352
|
// box could finish a build; these checks do.
|
|
338
|
-
const checks = releasePreflight({ config, configPath, root, repoRoot: doctorInvocation.repoRoot, platform: opts.platform, nativeLayout: resolvedNativeLayout });
|
|
353
|
+
const checks = releasePreflight({ config, configPath, root, repoRoot: doctorInvocation.repoRoot, platform: opts.platform, nativeLayout: resolvedNativeLayout, pinnedCargoLocks });
|
|
339
354
|
console.log("preflight:");
|
|
340
355
|
console.log(formatPreflight(checks));
|
|
341
356
|
const failures = preflightFailures(checks);
|
|
@@ -350,10 +365,15 @@ let prePackScanned = false; // set once a pre-pack payload scan has seen a nativ
|
|
|
350
365
|
// or app Cargo manifest enables devtools, qa-native, qa-control or dev-native (also in --dry-run).
|
|
351
366
|
// DevTools: refused for every app except an allow-listed one that declares `devtools: "content-only"` (devtools-policy.mjs).
|
|
352
367
|
let devtoolsAllowance = { mode: "denied" };
|
|
368
|
+
// The Rust a build compiles is checked from the manifest it compiles: targets.<os>.cargoManifest, else the declared
|
|
369
|
+
// nativeAssembly.cargoManifest. iosPackage.prepare commands run cargo for a native iOS app, so they are scanned like any build command.
|
|
370
|
+
const switchInputs = releaseSwitchInputs({ appRoot: root, config, target, prepare: iosSpec?.prepare ?? [], nativeIos });
|
|
371
|
+
if (switchInputs.problems.length) fail(`${config.app ?? "app"} ios release-switch scan cannot see what is built:\n- ${switchInputs.problems.join("\n- ")}`);
|
|
353
372
|
try {
|
|
354
373
|
devtoolsAllowance = resolveDevtoolsAllowance({ config, root });
|
|
355
374
|
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:
|
|
375
|
+
assertNoForbiddenBuildFeatures({ root, tier: "release/update", devtools: devtoolsAllowance, cargoManifest: switchInputs.cargoManifest, manifest: config.releaseSwitches?.manifest, commands: [
|
|
376
|
+
...switchInputs.commands,
|
|
357
377
|
{ label: "target.prePackage", command: target.prePackage },
|
|
358
378
|
{ label: "target.package", command: target.package },
|
|
359
379
|
{ label: "nativeAssembly.packageHook", command: nativeAssembly?.packageHook },
|
|
@@ -374,17 +394,18 @@ const releaseSwitchInventoryText = existsSync(releaseSwitchInventoryPath) ? read
|
|
|
374
394
|
try {
|
|
375
395
|
// A `node recipe-build.mjs --plan <JSON>` prePackage hides each step's `--features` in one argument: the closure and the
|
|
376
396
|
// 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 }));
|
|
397
|
+
const closureCommands = [target.prePackage, target.package, nativeAssembly?.packageHook, nativeFinalizer, ...commandList(target.postSign), target.gradle, ...switchInputs.cargoCommands].filter(Boolean).flatMap((c) => expandRecipeBuildCommands(c, { root }));
|
|
378
398
|
assertReleaseDependencies({ root, config, target, commands: closureCommands });
|
|
379
399
|
let treeText;
|
|
380
400
|
try {
|
|
381
401
|
const closure = resolveFeatureClosure({
|
|
382
402
|
root,
|
|
383
403
|
manifest: config.releaseSwitches?.manifest,
|
|
384
|
-
cargoManifest:
|
|
404
|
+
cargoManifest: switchInputs.cargoManifest,
|
|
385
405
|
rust: config.releaseSwitches?.rust,
|
|
386
406
|
features: config.releaseSwitches?.features ?? [],
|
|
387
|
-
triple
|
|
407
|
+
// iOS ships one device triple; "all" is refused by the build broker and would pull in desktop-only targets.
|
|
408
|
+
triple: target.cargoTarget ?? target.targetTriple ?? target.rustTarget ?? (opts.platform === "ios" ? "aarch64-apple-ios" : undefined),
|
|
388
409
|
commandTexts: closureCommands,
|
|
389
410
|
});
|
|
390
411
|
if (closure.skipped) console.log(`right-release: feature closure skipped (${closure.skipped})`);
|
|
@@ -394,9 +415,26 @@ try {
|
|
|
394
415
|
console.log(`right-release: dry run: ${error.message}`);
|
|
395
416
|
}
|
|
396
417
|
// remoteHost: rightkit-daemon `remote` is refused in the closure unless the app declares releaseSwitches.remoteHost: true.
|
|
397
|
-
const closureInput = { treeText, remoteHost: config.releaseSwitches?.remoteHost, commands: closureCommands.map((c) => commandTokens(c)) };
|
|
418
|
+
const closureInput = { treeText, devtools: devtoolsAllowance, remoteHost: config.releaseSwitches?.remoteHost, commands: closureCommands.map((c) => commandTokens(c)) };
|
|
398
419
|
if (releaseSwitchInventoryText != null) {
|
|
399
|
-
|
|
420
|
+
// iOS: only what the iPhone app compiles — the Xcode project and its local packages, plus the local crates in the
|
|
421
|
+
// resolved aarch64-apple-ios cargo graph (feature-aware, unlike the ownership closure). Other targets walk the app root.
|
|
422
|
+
let sourceRoots;
|
|
423
|
+
if (opts.platform === "ios") {
|
|
424
|
+
const repoTop = git(root, ["rev-parse", "--show-toplevel"]);
|
|
425
|
+
// Swift side only: the Rust side is the resolved cargo graph below. The ownership scope also folds in the whole
|
|
426
|
+
// nativeAssembly workspace, which would scan crates (desktop tools, the TUI) the iPhone never links.
|
|
427
|
+
const swiftSide = (iosOwnershipScope({ repoRoot: repoTop, appRoot: root, target: config.targets?.ios }) ?? []).filter((dir) => dir !== ".rightkit").map((dir) => path.join(repoTop, dir));
|
|
428
|
+
sourceRoots = [...new Set([...swiftSide, ...localCrateDirsFromTree(treeText, repoTop)])].sort();
|
|
429
|
+
// A prepare step can stage sources from anywhere (a web bundle, a generated resource) into the app; its working directory must be
|
|
430
|
+
// inside the scanned roots, or the release fails instead of shipping what the scan never saw. Add the directory to ownershipScope.
|
|
431
|
+
for (const step of config.targets?.ios?.iosPackage?.prepare ?? []) {
|
|
432
|
+
const cwd = path.resolve(root, step?.cwd ?? ".");
|
|
433
|
+
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`);
|
|
434
|
+
}
|
|
435
|
+
console.log(`right-release: source switch scan (ios): ${sourceRoots.map((dir) => path.relative(repoTop, dir) || ".").join(", ")}`);
|
|
436
|
+
}
|
|
437
|
+
assertNoReleaseSwitches({ ...closureInput, sourceRoot: root, sourceRoots, inventoryText: releaseSwitchInventoryText, appForbid: config.releaseSwitches?.forbid ?? [] }, "source switch inventory");
|
|
400
438
|
} else if (treeText != null || closureInput.commands.length) {
|
|
401
439
|
assertNoReleaseSwitches({ ...closureInput, appForbid: config.releaseSwitches?.forbid ?? [] }, "feature closure");
|
|
402
440
|
}
|
|
@@ -408,7 +446,7 @@ try {
|
|
|
408
446
|
const releaseLock = acquireReleaseLock(root, config.app ?? path.basename(root), opts.platform, opts.tier);
|
|
409
447
|
const mode = "package";
|
|
410
448
|
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`);
|
|
449
|
+
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
450
|
if (opts.upload && !target.publish && !target.upload) {
|
|
413
451
|
fail(`${config.app ?? "app"} has no ${opts.platform} upload config`);
|
|
414
452
|
}
|
|
@@ -524,6 +562,16 @@ if (opts.platform === "win" && target.signingContract === MSIX_SIGNING_CONTRACT)
|
|
|
524
562
|
receiptDir: path.resolve(process.env.RIGHT_RELEASE_STATE_ROOT ?? path.join(root, ".right-release", "receipts")),
|
|
525
563
|
allowContentOnlyDevtools: devtoolsAllowance.mode === "content-only",
|
|
526
564
|
});
|
|
565
|
+
} else if (opts.platform === "ios") {
|
|
566
|
+
// Archive and export with Xcode automatic signing in the GUI session, then the IPA verification and the receipt (macOS only;
|
|
567
|
+
// --dry-run prints the plan anywhere). Upload is a separate command: `right-release upload --platform ios`.
|
|
568
|
+
await runIosPackageRecipe({
|
|
569
|
+
root, app: config.app ?? path.basename(root), spec: iosSpec, dryRun: opts.dryRun,
|
|
570
|
+
receiptDir: path.resolve(process.env.RIGHT_RELEASE_STATE_ROOT ?? path.join(root, ".right-release", "receipts")),
|
|
571
|
+
scanFiles: (label, files, scanOptions) => assertNoQaControlBinaries(label, files, scanOptions),
|
|
572
|
+
// The build number counter outlives every release id: it lives in the vault state root that right-release build exports.
|
|
573
|
+
...(process.env.RIGHT_RELEASE_VAULT_STATE_ROOT ? { counterDir: path.join(path.resolve(process.env.RIGHT_RELEASE_VAULT_STATE_ROOT), "ios-build-numbers") } : {}),
|
|
574
|
+
});
|
|
527
575
|
} else if (nativeAssembly) {
|
|
528
576
|
if (nativeAssembly.packageHook) await runCommand(nativeAssembly.packageHook, root);
|
|
529
577
|
if (nativeFinalizer) {
|
|
@@ -647,7 +695,7 @@ function assertNoQaControlBinaries(label, files, { requireBinary = false, prePac
|
|
|
647
695
|
try {
|
|
648
696
|
const stats = assertNoQaControl(files, label, { requireBinary, allowContentOnlyDevtools: devtoolsAllowance.mode === "content-only" });
|
|
649
697
|
// 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)`);
|
|
698
|
+
assertNoReleaseSwitches({ binaryPaths: files, devtools: devtoolsAllowance, inventoryText: releaseSwitchInventoryText ?? undefined, appForbid: config.releaseSwitches?.forbid ?? [], requireBinary: false }, `${label} (release switches)`);
|
|
651
699
|
if (prePack && stats.binaries > 0) prePackScanned = true;
|
|
652
700
|
for (const opaque of stats.opaque) {
|
|
653
701
|
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 +956,8 @@ function hasNativeTargetSpecificPrePackage(target, packageHook) {
|
|
|
908
956
|
// publish so it cannot race an artifact, and is best-effort: a missing
|
|
909
957
|
// cargo-sweep or a sweep failure must never fail a release.
|
|
910
958
|
function sweepStaleRustArtifacts() {
|
|
959
|
+
// No Cargo host, nothing of ours to sweep.
|
|
960
|
+
if (!nativeLayout) return;
|
|
911
961
|
const candidates = [...new Set([root, workdir, nativeLayout.manifestDir, nativeLayout.workspaceRoot])];
|
|
912
962
|
const projects = candidates.filter((dir) => existsSync(path.join(dir, "target")));
|
|
913
963
|
for (const dir of projects) {
|
|
@@ -1432,6 +1482,8 @@ function expandEnv(value) {
|
|
|
1432
1482
|
// notices and the source offer, optionally run the app's stage command, and (before signing) check the staged files.
|
|
1433
1483
|
async function prepareMediaSidecar(root, { stage = true } = {}) {
|
|
1434
1484
|
if (!mediaSidecarConfig) return null;
|
|
1485
|
+
// Unreachable for iOS (mediaSidecar is mac and win only, refused above); a Cargo-less layout must still never be dereferenced.
|
|
1486
|
+
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
1487
|
let plan;
|
|
1436
1488
|
try {
|
|
1437
1489
|
const loaded = loadSidecarCatalog({ config: mediaSidecarConfig, root, manifestPath: nativeLayout.manifestPath });
|
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": [
|