@rightkit/release 0.2.138 → 0.2.140
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 +16 -0
- package/build-release.mjs +30 -21
- package/cache-policy.mjs +14 -0
- package/committed-source.mjs +2 -1
- package/dmg-build.mjs +10 -3
- package/hdiutil-detach.mjs +42 -0
- package/install-app.mjs +98 -37
- package/macos-notarization.mjs +21 -1
- package/managed-cargo.mjs +4 -1
- package/msix-appinstaller.mjs +24 -0
- package/msix-build.mjs +48 -12
- package/msix-config.mjs +70 -0
- package/msix-plan.mjs +68 -0
- package/msix-target.mjs +28 -0
- package/ownership.mjs +24 -0
- package/package.json +1 -1
- package/preflight.mjs +37 -23
- package/project-type.mjs +43 -0
- package/publish-update.mjs +11 -5
- package/release-invocation.mjs +8 -6
- package/release.mjs +55 -10
- package/rightkit-versions.json +18 -12
- package/source-gate.mjs +21 -0
- package/swift-build-context.mjs +43 -0
package/release.mjs
CHANGED
|
@@ -17,13 +17,17 @@ import {
|
|
|
17
17
|
resolveNativeFinalizerConfig,
|
|
18
18
|
validateNativeFinalizationOutput,
|
|
19
19
|
} from "./native-release-finalization.mjs";
|
|
20
|
+
import { packageCommandOptional, resolveProjectType, targetUsesMsix } from "./project-type.mjs";
|
|
21
|
+
import { MSIX_SIGNING_CONTRACT, assertValidMsixTarget } from "./msix-config.mjs";
|
|
22
|
+
import { buildMsix } from "./msix-build.mjs";
|
|
23
|
+
import { msixBuildOptions } from "./msix-target.mjs";
|
|
20
24
|
import { collectPreflight, formatPreflight, preflightFailures } from "./preflight.mjs";
|
|
21
25
|
import { patchTauriBundleType } from "./tauri-bundle-marker.mjs";
|
|
22
26
|
import { verifyNsisEmbeddedBinary } from "./nsis-payload.mjs";
|
|
23
27
|
import { terminateProcessTree } from "./heavy-command.mjs";
|
|
24
28
|
import { materializeHardeningEvidence } from "./hardening-evidence.mjs";
|
|
25
29
|
import { signingReceiptPath } from "./signing-receipts.mjs";
|
|
26
|
-
import { validateNotarizationReceipt } from "./macos-notarization.mjs";
|
|
30
|
+
import { notarizeAppBundle, validateNotarizationReceipt } from "./macos-notarization.mjs";
|
|
27
31
|
import { MACOS_PORTABLE_SIGNING_CONTRACT } from "./macos-signing-contract.mjs";
|
|
28
32
|
import { runConfiguredChecks } from "./release-checks.mjs";
|
|
29
33
|
import { resolveMacosDeveloperIdIdentity } from "./macos-signing-identity.mjs";
|
|
@@ -118,7 +122,10 @@ const workdir = path.resolve(root, config.workdir ?? ".");
|
|
|
118
122
|
const nativeLayout = resolveNativeCargoLayout({ appRoot: root, config });
|
|
119
123
|
const nativeAssembly = config.nativeAssembly;
|
|
120
124
|
const nativeFinalizer = resolveNativeFinalizerConfig(nativeAssembly);
|
|
121
|
-
|
|
125
|
+
// A SwiftPM app without a package.json has no Node package contract to validate.
|
|
126
|
+
if (!(resolveProjectType({ appRoot: root, config, target: config.targets?.[opts.platform] }) === "swiftpm" && !existsSync(path.join(root, "package.json")))) {
|
|
127
|
+
await validateRightKitPackageContract(root, config.app, config.hostedWorkflows);
|
|
128
|
+
}
|
|
122
129
|
validateRightKitCargoContract(root, RIGHTKIT_CARGO_ALLOWED, config.app ?? path.basename(root));
|
|
123
130
|
const target = config.targets?.[opts.platform];
|
|
124
131
|
if (!target) fail(`${config.app ?? "app"} has no ${opts.platform} release target`);
|
|
@@ -131,26 +138,32 @@ if (target.signed !== true) {
|
|
|
131
138
|
if (opts.platform === "win") {
|
|
132
139
|
const isNsis = target.signingContract === WINDOWS_NSIS_SIGNING_CONTRACT;
|
|
133
140
|
const isPortable = target.signingContract === WINDOWS_PORTABLE_SIGNING_CONTRACT;
|
|
134
|
-
|
|
141
|
+
const isMsix = target.signingContract === MSIX_SIGNING_CONTRACT;
|
|
142
|
+
if (!isNsis && !isPortable && !isMsix) fail(`${config.app ?? "app"} win must declare signingContract: ${WINDOWS_NSIS_SIGNING_CONTRACT}, ${WINDOWS_PORTABLE_SIGNING_CONTRACT} or ${MSIX_SIGNING_CONTRACT}`);
|
|
143
|
+
if (isMsix) {
|
|
144
|
+
try { assertValidMsixTarget(target, config.app ?? "app"); } catch (error) { fail(error.message); }
|
|
145
|
+
} else {
|
|
135
146
|
if (!target.prePackage?.cmd) fail(`${config.app ?? "app"} win must declare prePackage for the raw EXE build`);
|
|
136
147
|
if (!target.sign?.prePackageFiles?.length) fail(`${config.app ?? "app"} win must declare at least one sign.prePackageFiles raw EXE`);
|
|
137
148
|
if (isNsis && target.sign.prePackageFiles.length !== 1) fail(`${config.app ?? "app"} win NSIS must declare exactly one sign.prePackageFiles raw EXE`);
|
|
138
149
|
if (isNsis && !target.sign?.files?.length) fail(`${config.app ?? "app"} win NSIS must declare sign.files for installer signing`);
|
|
139
150
|
if (isNsis && target.sign.prePackageFiles.some((file) => target.sign.files.includes(file))) fail(`${config.app ?? "app"} win raw EXE and installer signing files must be distinct`);
|
|
140
151
|
if (isPortable && target.sign?.files?.length) fail(`${config.app ?? "app"} win portable release must sign every executable through sign.prePackageFiles and omit sign.files`);
|
|
152
|
+
}
|
|
141
153
|
}
|
|
142
154
|
if (opts.platform === "mac") {
|
|
143
155
|
if (target.signingContract !== MACOS_PORTABLE_SIGNING_CONTRACT) fail(`${config.app ?? "app"} mac must declare signingContract: ${MACOS_PORTABLE_SIGNING_CONTRACT}`);
|
|
144
156
|
if (!target.prePackage?.cmd) fail(`${config.app ?? "app"} mac must declare prePackage to materialize Developer ID signing files`);
|
|
145
157
|
if (!target.sign?.prePackageFiles?.length) fail(`${config.app ?? "app"} mac must declare at least one sign.prePackageFiles`);
|
|
146
158
|
if (!target.notarize?.file) fail(`${config.app ?? "app"} mac must declare notarize.file`);
|
|
159
|
+
if (target.notarize.appBeforePackage && !target.notarize.app) fail(`${config.app ?? "app"} mac notarize.appBeforePackage requires notarize.app`);
|
|
147
160
|
}
|
|
148
161
|
if (opts.upload && target.publishBlocked) fail(`${config.app ?? "app"} ${opts.platform} publish blocked: ${target.publishBlocked}`);
|
|
149
162
|
/**
|
|
150
163
|
* Assemble preflight inputs from the app's own files. Kept here (not in
|
|
151
164
|
* preflight.mjs) so the check module stays free of release.mjs's config shape.
|
|
152
165
|
*/
|
|
153
|
-
function releasePreflight({ config, configPath, root, repoRoot, platform, nativeLayout: suppliedNativeLayout = nativeLayout }) {
|
|
166
|
+
function releasePreflight({ config, configPath, root, repoRoot, platform, nativeLayout: suppliedNativeLayout = nativeLayout, projectTarget = config.targets?.[platform] }) {
|
|
154
167
|
let version;
|
|
155
168
|
try {
|
|
156
169
|
version = JSON.parse(readFileSync(path.join(root, "package.json"), "utf8")).version;
|
|
@@ -173,6 +186,8 @@ function releasePreflight({ config, configPath, root, repoRoot, platform, native
|
|
|
173
186
|
cargoLockPaths,
|
|
174
187
|
nativeLayout: suppliedNativeLayout,
|
|
175
188
|
targetLink: suppliedNativeLayout?.targetLink,
|
|
189
|
+
projectType: resolveProjectType({ appRoot: root, config, target: projectTarget }),
|
|
190
|
+
msix: targetUsesMsix(projectTarget),
|
|
176
191
|
});
|
|
177
192
|
}
|
|
178
193
|
|
|
@@ -234,7 +249,7 @@ if (opts.doctor) {
|
|
|
234
249
|
const releaseLock = acquireReleaseLock(root, config.app ?? path.basename(root), opts.platform, opts.tier);
|
|
235
250
|
const mode = "package";
|
|
236
251
|
const command = target.package;
|
|
237
|
-
if (!command) fail(`${config.app ?? "app"} has no ${opts.platform} ${mode} command`);
|
|
252
|
+
if (!command && !packageCommandOptional(target, resolveProjectType({ appRoot: root, config, target }))) fail(`${config.app ?? "app"} has no ${opts.platform} ${mode} command`);
|
|
238
253
|
if (opts.upload && !target.publish && !target.upload) {
|
|
239
254
|
fail(`${config.app ?? "app"} has no ${opts.platform} upload config`);
|
|
240
255
|
}
|
|
@@ -255,7 +270,17 @@ await runConfiguredChecks({
|
|
|
255
270
|
|
|
256
271
|
let nativeFinalization = null;
|
|
257
272
|
let bundleMarkerReceipts = [];
|
|
258
|
-
if (opts.platform === "win") {
|
|
273
|
+
if (opts.platform === "win" && target.signingContract === MSIX_SIGNING_CONTRACT) {
|
|
274
|
+
// MSIX order: prePackage builds + stages the payload; buildMsix signs inner PE files (DLLs, EXEs),
|
|
275
|
+
// packs the exact signed bytes, signs the package through sign-windows, verifies signer == Publisher,
|
|
276
|
+
// writes the App Installer route and the seal record. Never a second signer.
|
|
277
|
+
await runCommand(target.prePackage, root);
|
|
278
|
+
const options = msixBuildOptions({ root, target, app: config.app, dryRun: opts.dryRun, receipts: {
|
|
279
|
+
pe: windowsReceiptPath(root, "raw-exe"), msix: windowsReceiptPath(root, "installer"),
|
|
280
|
+
dir: path.dirname(windowsReceiptPath(root, "raw-exe")) } });
|
|
281
|
+
const built = buildMsix(options, { log: (event) => console.log(`right-release: ${JSON.stringify(event)}`) });
|
|
282
|
+
console.log(`right-release: msix ${built.dryRun ? "dry-run" : "built"} ${options.out}`);
|
|
283
|
+
} else if (opts.platform === "win") {
|
|
259
284
|
const rawFiles = target.sign.prePackageFiles.map((p) => path.resolve(root, p));
|
|
260
285
|
await runCommand(target.prePackage, root);
|
|
261
286
|
for (const file of rawFiles) await mustExist(file, `missing raw EXE signing artifact: ${file}`);
|
|
@@ -286,7 +311,11 @@ if (opts.platform === "win") {
|
|
|
286
311
|
// Resolve after prePackage: with storage governance the artifact may live only in RIGHT_RELEASE_ARTIFACT_DIR.
|
|
287
312
|
const signingFiles = target.sign.prePackageFiles.map((p) => resolveReleaseArtifactPath(root, p));
|
|
288
313
|
for (const file of signingFiles) await mustExist(file, `missing macOS Developer ID signing artifact: ${file}`);
|
|
289
|
-
if (runMacPhase("sign"))
|
|
314
|
+
if (runMacPhase("sign")) {
|
|
315
|
+
await signMacos(signingFiles, root);
|
|
316
|
+
// Opt-in (notarize.appBeforePackage): staple the .app itself before the package step copies it into the DMG.
|
|
317
|
+
if (target.notarize.appBeforePackage) await notarizeAppBeforePackage(root);
|
|
318
|
+
}
|
|
290
319
|
} else if (nativeAssembly) {
|
|
291
320
|
if (nativeAssembly.packageHook) await runCommand(nativeAssembly.packageHook, root);
|
|
292
321
|
if (nativeFinalizer) {
|
|
@@ -300,7 +329,7 @@ if (opts.platform === "win") {
|
|
|
300
329
|
}
|
|
301
330
|
}
|
|
302
331
|
|
|
303
|
-
if (opts.platform !== "mac" || runMacPhase("package")) await runCommand(command, root);
|
|
332
|
+
if (command && (opts.platform !== "mac" || runMacPhase("package"))) await runCommand(command, root);
|
|
304
333
|
|
|
305
334
|
for (const rel of target.artifacts ?? []) {
|
|
306
335
|
await mustExist(resolveReleaseArtifactPath(root, rel), `missing release artifact: ${rel}`);
|
|
@@ -373,7 +402,7 @@ releaseLock.release();
|
|
|
373
402
|
|
|
374
403
|
function usage(code, message) {
|
|
375
404
|
if (message) console.error(`right-release: ${message}\n`);
|
|
376
|
-
console.error("usage:
|
|
405
|
+
console.error("usage: right-release build --platform mac|win [--config right-release.config.mjs] (build is tier-neutral; tier applies only to: right-release upload --release <id> --platform mac|win --tier patch|update)");
|
|
377
406
|
process.exit(code);
|
|
378
407
|
}
|
|
379
408
|
|
|
@@ -846,7 +875,8 @@ async function notarizeMacos(archive, root) {
|
|
|
846
875
|
if (opts.dryRun) args.push("--dry-run");
|
|
847
876
|
args.push("--receipt", receipt);
|
|
848
877
|
if (target.notarize.profile) args.push("--profile", target.notarize.profile);
|
|
849
|
-
|
|
878
|
+
// With appBeforePackage the app already carries its own stapled ticket inside the packaged DMG.
|
|
879
|
+
if (target.notarize.app && !target.notarize.appBeforePackage) args.push("--app", resolveReleaseArtifactPath(root, target.notarize.app));
|
|
850
880
|
args.push(archive);
|
|
851
881
|
if (!opts.dryRun && existsSync(receipt)) unlinkSync(receipt);
|
|
852
882
|
console.log("right-release: notarizing macOS archive");
|
|
@@ -854,6 +884,21 @@ async function notarizeMacos(archive, root) {
|
|
|
854
884
|
if (!opts.dryRun) verifyMacosNotarizationReceipt(receipt, archive);
|
|
855
885
|
}
|
|
856
886
|
|
|
887
|
+
async function notarizeAppBeforePackage(root) {
|
|
888
|
+
const app = resolveReleaseArtifactPath(root, target.notarize.app);
|
|
889
|
+
const receipt = macosReceiptPath(root, "app-notarization");
|
|
890
|
+
const zip = `${app}.zip`;
|
|
891
|
+
console.log("right-release: notarizing and stapling macOS app before packaging");
|
|
892
|
+
if (opts.dryRun) { console.log(`dry-run: notarize app ${app} (zip ${zip}) receipt ${receipt}`); return; }
|
|
893
|
+
await mustExist(app, `missing macOS app bundle for notarize.app: ${app}`);
|
|
894
|
+
if (existsSync(receipt)) unlinkSync(receipt);
|
|
895
|
+
try {
|
|
896
|
+
notarizeAppBundle({ app, zip, receiptPath: receipt, profile: target.notarize.profile });
|
|
897
|
+
validateNotarizationReceipt({ receiptPath: receipt, archive: zip });
|
|
898
|
+
} catch (error) { fail(`app notarization failed: ${error.message}`); }
|
|
899
|
+
finally { rmSync(zip, { force: true }); }
|
|
900
|
+
}
|
|
901
|
+
|
|
857
902
|
function macosReceiptPath(root, phase) {
|
|
858
903
|
return signingReceiptPath({ appRoot: root, target, platform: "mac", phase, stateRoot: process.env.RIGHT_RELEASE_STATE_ROOT });
|
|
859
904
|
}
|
package/rightkit-versions.json
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"@rightkit/app-shell": "0.3.0",
|
|
17
17
|
"@rightkit/ax": "0.2.4",
|
|
18
18
|
"@rightkit/doc-render": "0.1.4",
|
|
19
|
-
"@rightkit/git": "0.2.
|
|
19
|
+
"@rightkit/git": "0.2.32",
|
|
20
20
|
"@rightkit/hooks": "0.1.2",
|
|
21
21
|
"@rightkit/i18n": "0.1.0",
|
|
22
22
|
"@rightkit/legal": "0.3.1",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"@rightkit/platform-ui": "0.2.0",
|
|
30
30
|
"@rightkit/qa": "0.4.0",
|
|
31
31
|
"@rightkit/qa-chrome": "0.1.3",
|
|
32
|
-
"@rightkit/release": "0.2.
|
|
32
|
+
"@rightkit/release": "0.2.140",
|
|
33
33
|
"@rightkit/settings-ui": "0.3.0",
|
|
34
34
|
"@rightkit/shell": "0.1.0",
|
|
35
35
|
"@rightkit/tauri": "0.2.0",
|
|
@@ -72,7 +72,8 @@
|
|
|
72
72
|
"0.2.27",
|
|
73
73
|
"0.2.28",
|
|
74
74
|
"0.2.29",
|
|
75
|
-
"0.2.30"
|
|
75
|
+
"0.2.30",
|
|
76
|
+
"0.2.31"
|
|
76
77
|
],
|
|
77
78
|
"@rightkit/legal-ui": [
|
|
78
79
|
"0.1.0",
|
|
@@ -168,7 +169,9 @@
|
|
|
168
169
|
"0.2.134",
|
|
169
170
|
"0.2.135",
|
|
170
171
|
"0.2.136",
|
|
171
|
-
"0.2.137"
|
|
172
|
+
"0.2.137",
|
|
173
|
+
"0.2.138",
|
|
174
|
+
"0.2.139"
|
|
172
175
|
],
|
|
173
176
|
"@rightkit/qa": [
|
|
174
177
|
"0.1.0",
|
|
@@ -275,11 +278,11 @@
|
|
|
275
278
|
"rightkit-framed-sidecar": "0.1.0",
|
|
276
279
|
"rightkit-fs": "0.3.2",
|
|
277
280
|
"rightkit-fsindex": "0.1.0",
|
|
278
|
-
"rightkit-http": "0.2.
|
|
281
|
+
"rightkit-http": "0.2.4",
|
|
279
282
|
"rightkit-jobs": "0.2.6",
|
|
280
283
|
"rightkit-license": "0.1.3",
|
|
281
284
|
"rightkit-logs": "0.2.0",
|
|
282
|
-
"rightkit-mcp": "0.5.
|
|
285
|
+
"rightkit-mcp": "0.5.2",
|
|
283
286
|
"rightkit-media": "0.2.4",
|
|
284
287
|
"rightkit-models": "0.2.7",
|
|
285
288
|
"rightkit-oauth": "0.3.0",
|
|
@@ -332,11 +335,11 @@
|
|
|
332
335
|
"rightkit-framed-sidecar": "0.1.0",
|
|
333
336
|
"rightkit-fs": "0.3.2",
|
|
334
337
|
"rightkit-fsindex": "0.1.0",
|
|
335
|
-
"rightkit-http": "0.2.
|
|
338
|
+
"rightkit-http": "0.2.4",
|
|
336
339
|
"rightkit-jobs": "0.2.6",
|
|
337
340
|
"rightkit-license": "0.1.3",
|
|
338
341
|
"rightkit-logs": "0.2.0",
|
|
339
|
-
"rightkit-mcp": "0.5.
|
|
342
|
+
"rightkit-mcp": "0.5.2",
|
|
340
343
|
"rightkit-media": "0.2.4",
|
|
341
344
|
"rightkit-models": "0.2.7",
|
|
342
345
|
"rightkit-oauth": "0.3.0",
|
|
@@ -367,7 +370,7 @@
|
|
|
367
370
|
"@rightkit/app-shell": "0.3.0",
|
|
368
371
|
"@rightkit/ax": "0.2.4",
|
|
369
372
|
"@rightkit/doc-render": "0.1.4",
|
|
370
|
-
"@rightkit/git": "0.2.
|
|
373
|
+
"@rightkit/git": "0.2.32",
|
|
371
374
|
"@rightkit/hooks": "0.1.2",
|
|
372
375
|
"@rightkit/i18n": "0.1.0",
|
|
373
376
|
"@rightkit/legal": "0.3.1",
|
|
@@ -380,7 +383,7 @@
|
|
|
380
383
|
"@rightkit/platform-ui": "0.2.0",
|
|
381
384
|
"@rightkit/qa": "0.4.0",
|
|
382
385
|
"@rightkit/qa-chrome": "0.1.3",
|
|
383
|
-
"@rightkit/release": "0.2.
|
|
386
|
+
"@rightkit/release": "0.2.140",
|
|
384
387
|
"@rightkit/settings-ui": "0.3.0",
|
|
385
388
|
"@rightkit/shell": "0.1.0",
|
|
386
389
|
"@rightkit/tauri": "0.2.0",
|
|
@@ -432,7 +435,8 @@
|
|
|
432
435
|
"0.3.0",
|
|
433
436
|
"0.3.1",
|
|
434
437
|
"0.4.0",
|
|
435
|
-
"0.5.0"
|
|
438
|
+
"0.5.0",
|
|
439
|
+
"0.5.1"
|
|
436
440
|
],
|
|
437
441
|
"rightkit-media": [
|
|
438
442
|
"0.2.2",
|
|
@@ -504,7 +508,9 @@
|
|
|
504
508
|
"rightkit-http": [
|
|
505
509
|
"0.1.0",
|
|
506
510
|
"0.2.0",
|
|
507
|
-
"0.2.1"
|
|
511
|
+
"0.2.1",
|
|
512
|
+
"0.2.2",
|
|
513
|
+
"0.2.3"
|
|
508
514
|
],
|
|
509
515
|
"rightkit-fs": [
|
|
510
516
|
"0.1.0",
|
package/source-gate.mjs
CHANGED
|
@@ -10,8 +10,29 @@
|
|
|
10
10
|
// real risk is shipping stale, dirty, or untested source. Distribution trust still
|
|
11
11
|
// comes from OS code signing, notarization, and the Tauri updater key — untouched.
|
|
12
12
|
|
|
13
|
+
// right-release's own per-platform run lock (`.right-release/locks/*`, every platform) is
|
|
14
|
+
// written before the later clean-tree preflights run. Only untracked files directly in that lock directory are
|
|
15
|
+
// exempt, so repos that do not ignore `.right-release/` still pass, and nothing else in
|
|
16
|
+
// the vault (sealed output, receipts, state) or elsewhere is ever excused.
|
|
17
|
+
const OWN_LOCK_RECORD = /^\?\? (?:[^\0]*\/)?\.right-release\/locks\/[^\0/]+$/;
|
|
18
|
+
|
|
19
|
+
/** Remove right-release's own untracked lock-file records from NUL-separated porcelain v1 output. */
|
|
20
|
+
export function stripOwnLockRecords(status) {
|
|
21
|
+
const records = status.split("\0");
|
|
22
|
+
const kept = [];
|
|
23
|
+
for (let index = 0; index < records.length; index += 1) {
|
|
24
|
+
const record = records[index];
|
|
25
|
+
if (!record) continue;
|
|
26
|
+
if (OWN_LOCK_RECORD.test(record)) continue;
|
|
27
|
+
kept.push(record);
|
|
28
|
+
if (/^[RC]/.test(record) || /^.[RC]/.test(record)) kept.push(records[++index] ?? "");
|
|
29
|
+
}
|
|
30
|
+
return kept.length ? `${kept.join("\0")}\0` : "";
|
|
31
|
+
}
|
|
32
|
+
|
|
13
33
|
export function assertCleanSource({ status, commandId = "release" }) {
|
|
14
34
|
if (typeof status !== "string") throw new Error("release source status is required");
|
|
35
|
+
status = stripOwnLockRecords(status);
|
|
15
36
|
if (status.length !== 0) {
|
|
16
37
|
const files = status.split("\0").filter(Boolean).map((entry) => entry.slice(3)).slice(0, 10);
|
|
17
38
|
throw new Error(`${commandId} requires a clean working tree; commit or stash first:\n${files.map((file) => `- ${file}`).join("\n")}`);
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Everything the sealed `build` path does differently for a SwiftPM target, as pure/stub-friendly
|
|
2
|
+
// functions so build-release.mjs only branches once. Rust-only steps (rustc/cargo probes, Cargo lock
|
|
3
|
+
// identity, broker cargo target, Cache V2 shared Rust cache, target bridge, CARGO/SCCACHE env) are skipped.
|
|
4
|
+
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { resolveSwiftCacheIdentity } from "./cache-policy.mjs";
|
|
7
|
+
|
|
8
|
+
const RUST_ENV = ["CARGO_HOME", "CARGO_TARGET_DIR", "RUSTC_WRAPPER", "SCCACHE_DIR", "SCCACHE_BASEDIRS", "SCCACHE_CACHE_SIZE", "SCCACHE_IGNORE_SERVER_IO_ERROR", "RUSTFLAGS", "RIGHT_RELEASE_CACHE_OWNER"];
|
|
9
|
+
|
|
10
|
+
export function swiftArchitecture({ target = {}, platform, hostArch = process.arch }) {
|
|
11
|
+
const arch = target.msix?.identity?.architecture ?? target.architecture ?? (hostArch === "arm64" ? "arm64" : "x64");
|
|
12
|
+
return platform === "mac" ? (arch === "x64" ? "x86_64" : arch) : arch;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** exec(cmd, args) -> stdout string. Throws when swift is missing. */
|
|
16
|
+
export function collectSwiftToolVersions({ packageManager, exec, nodeVersion = process.version }) {
|
|
17
|
+
const swift = exec("swift", ["--version"]).trim();
|
|
18
|
+
if (!swift) throw new Error("swift --version printed nothing");
|
|
19
|
+
return { node: nodeVersion, packageManager: packageManager ? `${packageManager} ${exec(packageManager, ["--version"]).trim()}` : null, swift, cargo: null, rustc: null, rustHost: null, sccache: null };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Plan for a swiftpm sealed build: identity, forced non-shared cache mode, no broker cargo target, no target
|
|
24
|
+
* bridge, a plain build/cache dir under the vault, and an environment with every Rust variable removed.
|
|
25
|
+
*/
|
|
26
|
+
export function planSwiftBuild({ appRoot, vaultRoot, platform, target, toolVersions, profile = "release", hostArch }) {
|
|
27
|
+
const architecture = swiftArchitecture({ target, platform, hostArch });
|
|
28
|
+
const identity = resolveSwiftCacheIdentity({ swiftVersion: toolVersions.swift, packageResolvedPath: path.join(appRoot, "Package.resolved"), architecture, profile: target.profile ?? profile });
|
|
29
|
+
const buildDir = path.resolve(vaultRoot, "cache", "swift-build", platform, identity.fingerprint);
|
|
30
|
+
return {
|
|
31
|
+
identity, architecture, buildDir,
|
|
32
|
+
cacheMode: "legacy",
|
|
33
|
+
managedCargoTarget: null,
|
|
34
|
+
targetBridge: { run: (fn) => fn(), ensure() {} },
|
|
35
|
+
sanitizeEnv(env) {
|
|
36
|
+
const next = { ...env };
|
|
37
|
+
for (const name of RUST_ENV) delete next[name];
|
|
38
|
+
next.RIGHT_RELEASE_SWIFT_BUILD_DIR = buildDir;
|
|
39
|
+
return next;
|
|
40
|
+
},
|
|
41
|
+
watchDirs: [buildDir, path.join(appRoot, ".build")],
|
|
42
|
+
};
|
|
43
|
+
}
|