@rightkit/release 0.6.16 → 0.6.21
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 +7 -3
- package/admission-host.mjs +9 -5
- package/app-closure.mjs +9 -3
- package/bin/darwin/rightkit-swap +0 -0
- package/bin/rightkit-gate.mjs +481 -103
- package/bin/rightkit-linux-setup.mjs +15 -0
- package/broker-admission.mjs +184 -0
- package/broker-wire.mjs +15 -3
- package/build-launch.mjs +4 -1
- package/build-ownership.mjs +125 -36
- package/build-release.mjs +39 -7
- package/cargo-contract.mjs +4 -1
- package/cargo-guard.mjs +25 -5
- package/cargo-target-shim.mjs +56 -7
- package/clippy-route.mjs +8 -7
- package/crate-graph.mjs +5 -1
- package/credential-provider.mjs +174 -0
- package/deterministic-scans.mjs +253 -42
- package/development.mjs +8 -0
- package/disk-policy.mjs +2 -2
- package/gate-executor.mjs +5 -3
- package/gate-stages.mjs +34 -3
- package/gradle-lock.mjs +206 -0
- package/heavy-command.mjs +33 -2
- package/linux-artifacts.mjs +2 -1
- package/linux-setup.mjs +106 -0
- package/local-publish.mjs +21 -10
- package/local-qualify.mjs +9 -3
- package/managed-cargo.mjs +54 -18
- package/ownership-lockfiles.mjs +14 -3
- package/ownership.mjs +5 -1
- package/package.json +17 -11
- package/protected-credentials.mjs +13 -5
- package/publication-gate.mjs +9 -4
- package/python-lock.mjs +209 -0
- package/qa-admission-policy.json +32 -0
- package/qa-layout.mjs +229 -0
- package/qa-runner.mjs +156 -0
- package/release-artifact-path.mjs +29 -3
- package/release-config.mjs +1 -1
- package/release-qa-command.mjs +58 -8
- package/release-qa-policy.mjs +5 -3
- package/release-source-identity.mjs +1 -0
- package/release.mjs +10 -2
- package/rightkit-ownership.json +1465 -79
- package/rightkit-registry-resolver.mjs +7 -3
- package/rightkit-versions.json +173 -36
- package/runtime-budget-classes.json +4 -0
- package/runtime-budgets.mjs +5 -1
- package/shell-contract.mjs +1 -1
- package/snapshot-broker.mjs +114 -9
- package/snapshot-check.mjs +6 -1
- package/snapshot-request.mjs +20 -3
- package/snapshot.mjs +11 -4
- package/stage-commands.mjs +2 -1
- package/stage-ledger.mjs +15 -0
- package/stage-sidecar-command.mjs +7 -1
- package/stage-sidecar.mjs +17 -3
- package/suite-signing.mjs +40 -4
- package/swift-lock.mjs +334 -0
- package/test-command.mjs +13 -3
- package/tool-build.mjs +156 -0
- package/unit-gate.mjs +9 -2
- package/windows-identity-plan.mjs +119 -0
- package/windows-identity.mjs +136 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
// The Windows package-identity (sparse MSIX) producer: the pure half. An app declares it in its release config as
|
|
2
|
+
//
|
|
3
|
+
// windows: { identity: {
|
|
4
|
+
// manifest: "src-tauri/windows/identity/AppxManifest.xml", // checked-in template; only Identity/@Version is stamped
|
|
5
|
+
// out: "src-tauri/windows/identity/dist", // gets <package> and external-assets/Assets/*; must lie under a declared compile input
|
|
6
|
+
// package: "HeardRight.Identity.msix",
|
|
7
|
+
// version: "0.1.33", // optional; default is the config's `version` (3 or 4 numeric parts)
|
|
8
|
+
// assets: {
|
|
9
|
+
// files: [{ from: "src-tauri/icons/StoreLogo.png", to: "HeardRightStoreLogo.png" }, ...], // into the package AND external-assets/Assets
|
|
10
|
+
// scaled: { svg: "src-tauri/icons/heardright-icon.svg", sizes: [16, 20, 24], name: "HeardRightSquare44x44Logo.targetsize-{size}_altform-unplated.png" },
|
|
11
|
+
// },
|
|
12
|
+
// } }
|
|
13
|
+
//
|
|
14
|
+
// The gate runs it (windows-identity.mjs) inside the door's snapshot before the compile, on Windows only. This module holds no I/O of its own
|
|
15
|
+
// beyond config text matching: validation, version stamping, the step order, the expected package contents, and the write attribution.
|
|
16
|
+
|
|
17
|
+
import path from "node:path";
|
|
18
|
+
import { diffEntries } from "./stage-ledger.mjs";
|
|
19
|
+
|
|
20
|
+
export const WINDOWS_IDENTITY_KEYS = Object.freeze(["manifest", "out", "package", "version", "assets"]);
|
|
21
|
+
export const WINDOWS_IDENTITY_STEPS = Object.freeze(["stage-manifest", "stage-assets", "render-scaled", "makepri-config", "makepri-new", "pack", "unpack-roundtrip", "verify-roundtrip", "verify-pri", "sign", "cleanup"]);
|
|
22
|
+
/** Scratch lives under `out` and is removed before the gate walks it. */
|
|
23
|
+
export const WORK_DIR = ".identity-work";
|
|
24
|
+
|
|
25
|
+
const isObject = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
|
|
26
|
+
const posix = (v) => String(v).replaceAll("\\", "/");
|
|
27
|
+
const insideApp = (v) => typeof v === "string" && v.trim() !== "" && !path.isAbsolute(v) && !/^[A-Za-z]:/.test(v) && !posix(v).split("/").includes("..");
|
|
28
|
+
const flatName = (v, ext) => typeof v === "string" && /^[A-Za-z0-9._{}-]+$/.test(v) && v.toLowerCase().endsWith(ext) && !v.startsWith(".");
|
|
29
|
+
|
|
30
|
+
/** Does this config text declare `windows.identity`? Text match like committedWebBuildConfigs: a `.mjs` config is code and is never imported by the gate. */
|
|
31
|
+
export const declaresWindowsIdentity = (text) => /(^|[\s,{])["']?windows["']?\s*:\s*\{[^]*?["']?identity["']?\s*:\s*\{/.test(String(text));
|
|
32
|
+
|
|
33
|
+
/** The release configs of `commit` under `appDir` that declare `windows.identity`, read from `base` (the commit's config decides, never the checkout's). */
|
|
34
|
+
export function committedWindowsIdentityConfigs({ readFile, listConfigs, base }) {
|
|
35
|
+
return listConfigs.filter((f) => { try { return declaresWindowsIdentity(readFile(path.join(base, f))); } catch { return false; } });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** `x.y.z` or `x.y.z.w` numeric (each <= 65535) to the four-part MSIX version; anything else (a prerelease suffix) is refused. */
|
|
39
|
+
export function toMsixVersion(version) {
|
|
40
|
+
const parts = String(version ?? "").split(".");
|
|
41
|
+
if ((parts.length !== 3 && parts.length !== 4) || !parts.every((p) => /^\d{1,5}$/.test(p) && Number(p) <= 65535)) {
|
|
42
|
+
throw Object.assign(new Error(`version "${version}" is not 3 or 4 numeric segments (<=65535); cannot derive an MSIX package version (no prerelease or build suffix)`), { code: "RK_WINDOWS_IDENTITY" });
|
|
43
|
+
}
|
|
44
|
+
return (parts.length === 4 ? parts : [...parts, "0"]).join(".");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Stamp the real version into the template's `<Identity>` element only; returns `{ xml, name, publisher }`. */
|
|
48
|
+
export function stampManifest(xml, msixVersion) {
|
|
49
|
+
const tag = String(xml).match(/<Identity\b[^>]*\/>/)?.[0];
|
|
50
|
+
if (!tag) throw Object.assign(new Error("the manifest has no <Identity ... /> element"), { code: "RK_WINDOWS_IDENTITY" });
|
|
51
|
+
if (!/\bVersion="[^"]*"/.test(tag)) throw Object.assign(new Error("the manifest <Identity> has no Version attribute"), { code: "RK_WINDOWS_IDENTITY" });
|
|
52
|
+
const name = tag.match(/\bName="([^"]+)"/)?.[1];
|
|
53
|
+
if (!name) throw Object.assign(new Error("the manifest <Identity> has no Name attribute"), { code: "RK_WINDOWS_IDENTITY" });
|
|
54
|
+
return { xml: String(xml).replace(tag, tag.replace(/\bVersion="[^"]*"/, `Version="${msixVersion}"`)), name, publisher: tag.match(/\bPublisher="([^"]*)"/)?.[1] ?? null };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** The declared block, validated. Returns `{ problems, plan }` (plan null with problems). Paths stay app-relative posix. */
|
|
58
|
+
export function planWindowsIdentity(config) {
|
|
59
|
+
const problems = [];
|
|
60
|
+
const block = config?.windows?.identity;
|
|
61
|
+
if (!isObject(block)) return { problems: ["the config declares no windows.identity"], plan: null };
|
|
62
|
+
if (isObject(config.windows)) for (const k of Object.keys(config.windows).filter((k) => k !== "identity")) problems.push(`windows has unknown key ${k} (allowed: identity)`);
|
|
63
|
+
for (const k of Object.keys(block).filter((k) => !WINDOWS_IDENTITY_KEYS.includes(k))) problems.push(`windows.identity has unknown key ${k} (allowed: ${WINDOWS_IDENTITY_KEYS.join(", ")})`);
|
|
64
|
+
if (!insideApp(block.manifest)) problems.push("windows.identity.manifest must be a relative path inside the app");
|
|
65
|
+
if (!insideApp(block.out)) problems.push("windows.identity.out must be a relative path inside the app");
|
|
66
|
+
if (!flatName(block.package, ".msix")) problems.push("windows.identity.package must be a flat .msix file name");
|
|
67
|
+
const version = block.version ?? config?.version;
|
|
68
|
+
let msixVersion = null;
|
|
69
|
+
try { msixVersion = toMsixVersion(version); } catch (e) { problems.push(`windows.identity version: ${e.message}`); }
|
|
70
|
+
const files = block.assets?.files;
|
|
71
|
+
if (!Array.isArray(files) || !files.length) problems.push("windows.identity.assets.files must list at least one { from, to } PNG");
|
|
72
|
+
const seen = new Set();
|
|
73
|
+
for (const [i, f] of (Array.isArray(files) ? files : []).entries()) {
|
|
74
|
+
if (!isObject(f) || !insideApp(f.from) || !flatName(f.to, ".png")) { problems.push(`windows.identity.assets.files[${i}] needs from (app-relative) and to (flat .png name)`); continue; }
|
|
75
|
+
if (seen.has(f.to.toLowerCase())) problems.push(`windows.identity.assets.files[${i}].to ${f.to} repeats`);
|
|
76
|
+
seen.add(f.to.toLowerCase());
|
|
77
|
+
}
|
|
78
|
+
const scaled = block.assets?.scaled;
|
|
79
|
+
if (scaled !== undefined) {
|
|
80
|
+
if (!isObject(scaled) || !insideApp(scaled.svg) || !Array.isArray(scaled.sizes) || !scaled.sizes.length || scaled.sizes.some((s) => !Number.isInteger(s) || s < 1 || s > 1024) || !flatName(scaled.name, ".png") || !String(scaled.name).includes("{size}")) {
|
|
81
|
+
problems.push("windows.identity.assets.scaled needs svg (app-relative), sizes (integers 1..1024) and name (flat .png containing {size})");
|
|
82
|
+
} else for (const s of scaled.sizes) if (seen.has(scaled.name.replace("{size}", s).toLowerCase())) problems.push(`windows.identity.assets.scaled name for size ${s} collides with a files entry`);
|
|
83
|
+
}
|
|
84
|
+
if (problems.length) return { problems, plan: null };
|
|
85
|
+
return {
|
|
86
|
+
problems,
|
|
87
|
+
plan: {
|
|
88
|
+
manifest: posix(block.manifest), out: posix(block.out).replace(/\/+$/, ""), package: block.package, msixVersion,
|
|
89
|
+
files: files.map((f) => ({ from: posix(f.from), to: f.to })),
|
|
90
|
+
scaled: scaled ? { svg: posix(scaled.svg), sizes: [...scaled.sizes], name: scaled.name } : null,
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Every package-relative file the staged tree must contain (and the external Assets must mirror, minus the manifest and PRI). */
|
|
96
|
+
export const scaledNames = (plan) => (plan.scaled ? plan.scaled.sizes.map((s) => plan.scaled.name.replace("{size}", String(s))) : []);
|
|
97
|
+
export const assetNames = (plan) => [...plan.files.map((f) => f.to), ...scaledNames(plan)];
|
|
98
|
+
export const expectedPackageFiles = (plan) => ["AppxManifest.xml", "resources.pri", ...assetNames(plan).map((n) => `Assets/${n}`)];
|
|
99
|
+
/** The PRI resource URIs the manifest-backed (base) assets must resolve to: the taskbar resolves them, presence alone is not enough. */
|
|
100
|
+
export const expectedPriUris = (plan, identityName) => plan.files.map((f) => `ms-resource://${identityName}/Files/Assets/${f.to}`);
|
|
101
|
+
|
|
102
|
+
/** The ordered steps; `scaled` steps are omitted when no scaled renders are declared. */
|
|
103
|
+
export const windowsIdentitySteps = (plan) => WINDOWS_IDENTITY_STEPS.filter((s) => s !== "render-scaled" || plan.scaled);
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Write attribution: everything the producer added or changed lies under its `out` (which must itself lie under a declared compile input),
|
|
107
|
+
* nothing was removed, and the package exists. `declared` and `out` are snapshot-relative posix paths; `out` is the producer's out dir.
|
|
108
|
+
*/
|
|
109
|
+
export function identityOutputProblems({ before, after, out, declared, packageFile }) {
|
|
110
|
+
const o = posix(out).replace(/\/+$/, "");
|
|
111
|
+
const roots = declared.map((x) => posix(x).replace(/\/+$/, ""));
|
|
112
|
+
const problems = [];
|
|
113
|
+
if (!roots.some((r) => o === r || o.startsWith(`${r}/`))) problems.push(`windows.identity.out ${o} lies under no declared compile input (declare it in .rightkit/compile-inputs.json)`);
|
|
114
|
+
const d = diffEntries(before, after);
|
|
115
|
+
for (const p of [...d.added, ...d.changed]) if (!(p === o || p.startsWith(`${o}/`))) problems.push(`${p} was written by the Windows identity build but lies outside its out ${o}`);
|
|
116
|
+
for (const p of d.removed) if (!(p === o || p.startsWith(`${o}/`))) problems.push(`${p} was removed by the Windows identity build but lies outside its out ${o}`);
|
|
117
|
+
if (packageFile && !after.some((e) => e.path === `${o}/${packageFile}`)) problems.push(`${o}/${packageFile} was not produced`);
|
|
118
|
+
return problems;
|
|
119
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// `node windows-identity.mjs --config <file> [--json] [--dry-run]`: builds the app's Windows package-identity (sparse) MSIX from its release
|
|
3
|
+
// config's `windows.identity` (windows-identity-plan.mjs), the producer the app's own build-windows-identity.mjs used to be. Run by the gate
|
|
4
|
+
// inside the door's snapshot before the compile, on Windows only; under any other host it does nothing.
|
|
5
|
+
//
|
|
6
|
+
// Order (the archived script's, kept): stamp the manifest version into a staged copy; stage the asset PNGs into the package AND the external
|
|
7
|
+
// Assets dir (a sparse package resolves its logo pixels from the install location); render the scaled variants from the SVG; makepri builds
|
|
8
|
+
// resources.pri; makeappx packs with /nv (the identity package ships no executable by design); unpack round-trip; the PRI must index the
|
|
9
|
+
// manifest-backed assets; then the package is signed through sign-windows.mjs, the one provisioned Azure Artifact Signing path. This module
|
|
10
|
+
// never reads a credential, never authenticates, writes no signing receipt, and removes its scratch so only the package and external-assets remain.
|
|
11
|
+
import { copyFileSync, existsSync, mkdirSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
12
|
+
import { createRequire } from "node:module";
|
|
13
|
+
import path from "node:path";
|
|
14
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
15
|
+
import { spawnSync } from "./background-process.mjs";
|
|
16
|
+
import { loadAppConfig } from "./command-spec.mjs";
|
|
17
|
+
import { findWindowsSdkTool } from "./preflight.mjs";
|
|
18
|
+
import { WORK_DIR, expectedPackageFiles, expectedPriUris, planWindowsIdentity, scaledNames, stampManifest, windowsIdentitySteps } from "./windows-identity-plan.mjs";
|
|
19
|
+
import { webBuildDoorRefusal } from "./web-build.mjs";
|
|
20
|
+
|
|
21
|
+
const SIGN_WINDOWS = path.join(path.dirname(fileURLToPath(import.meta.url)), "sign-windows.mjs");
|
|
22
|
+
const fail = (message) => { throw Object.assign(new Error(message), { code: "RK_WINDOWS_IDENTITY" }); };
|
|
23
|
+
|
|
24
|
+
/** The default SVG renderer: the app's own `@resvg/resvg-js` (resolved from the app root, never RightKit's). Returns a PNG Buffer. */
|
|
25
|
+
export function appResvgRenderer(appRoot) {
|
|
26
|
+
const { Resvg } = createRequire(path.join(appRoot, "package.json"))("@resvg/resvg-js");
|
|
27
|
+
return (svg, size) => new Resvg(svg, { background: "rgba(0,0,0,0)", fitTo: { mode: "width", value: size } }).render().asPng();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Build the package. `deps` are injectable: `platform`, `run(cmd, args)`, `findTool`, `env`, `render(svg, size)`, `sign(file)`, `log`.
|
|
32
|
+
* Returns `{ skipped, steps, out, package, files }` (`files` app-relative posix). Skips (no writes) when the package already exists.
|
|
33
|
+
*/
|
|
34
|
+
export function buildWindowsIdentity({ plan, appRoot }, deps = {}) {
|
|
35
|
+
const { platform = process.platform, run = spawnSync, findTool = findWindowsSdkTool, env = process.env, render = null, log = (m) => console.error(`[windows-identity] ${m}`) } = deps;
|
|
36
|
+
const at = (p) => path.resolve(appRoot, p);
|
|
37
|
+
const outDir = at(plan.out);
|
|
38
|
+
const msix = path.join(outDir, plan.package);
|
|
39
|
+
const steps = windowsIdentitySteps(plan);
|
|
40
|
+
if (platform !== "win32") return { skipped: "not-windows", steps, out: plan.out, package: plan.package, files: [] };
|
|
41
|
+
if (existsSync(msix)) return { skipped: "present", steps, out: plan.out, package: plan.package, files: [] };
|
|
42
|
+
const tool = (name, envName) => { const p = env[envName] || findTool(name)?.toolPath; if (!p || !existsSync(p)) fail(`${name} not found; install the Windows SDK or set ${envName}`); return p; };
|
|
43
|
+
const makeappx = tool("makeappx.exe", "MAKEAPPX_PATH");
|
|
44
|
+
const makepri = tool("makepri.exe", "MAKEPRI_PATH");
|
|
45
|
+
const checked = (cmd, args, what) => { const r = run(cmd, args, { encoding: "utf8", windowsHide: true, timeout: 600000 }); if (r?.error || r?.status !== 0) fail(`${what} failed (${r?.error?.message ?? `exit ${r?.status}`}${r?.stderr ? `: ${String(r.stderr).trim()}` : ""})`); return r; };
|
|
46
|
+
const work = path.join(outDir, WORK_DIR);
|
|
47
|
+
const stage = path.join(work, "stage");
|
|
48
|
+
const external = path.join(outDir, "external-assets", "Assets");
|
|
49
|
+
try {
|
|
50
|
+
rmSync(work, { recursive: true, force: true });
|
|
51
|
+
mkdirSync(path.join(stage, "Assets"), { recursive: true });
|
|
52
|
+
rmSync(path.join(outDir, "external-assets"), { recursive: true, force: true });
|
|
53
|
+
mkdirSync(external, { recursive: true });
|
|
54
|
+
const manifestSource = at(plan.manifest);
|
|
55
|
+
if (!existsSync(manifestSource)) fail(`missing manifest ${plan.manifest}`);
|
|
56
|
+
const stamped = stampManifest(readFileSync(manifestSource, "utf8"), plan.msixVersion);
|
|
57
|
+
writeFileSync(path.join(stage, "AppxManifest.xml"), stamped.xml, "utf8");
|
|
58
|
+
log(`staged manifest ${stamped.name} Version="${plan.msixVersion}"`);
|
|
59
|
+
for (const f of plan.files) {
|
|
60
|
+
if (!existsSync(at(f.from))) fail(`missing asset ${f.from}`);
|
|
61
|
+
copyFileSync(at(f.from), path.join(stage, "Assets", f.to));
|
|
62
|
+
copyFileSync(at(f.from), path.join(external, f.to));
|
|
63
|
+
}
|
|
64
|
+
if (plan.scaled) {
|
|
65
|
+
if (!existsSync(at(plan.scaled.svg))) fail(`missing svg ${plan.scaled.svg}`);
|
|
66
|
+
const svg = readFileSync(at(plan.scaled.svg), "utf8");
|
|
67
|
+
const renderer = render ?? appResvgRenderer(appRoot);
|
|
68
|
+
for (const size of plan.scaled.sizes) {
|
|
69
|
+
const png = renderer(svg, size);
|
|
70
|
+
const name = plan.scaled.name.replace("{size}", String(size));
|
|
71
|
+
writeFileSync(path.join(stage, "Assets", name), png);
|
|
72
|
+
writeFileSync(path.join(external, name), png);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const priConfig = path.join(work, "priconfig.xml");
|
|
76
|
+
checked(makepri, ["createconfig", "/cf", priConfig, "/dq", "en-US", "/o"], "makepri createconfig");
|
|
77
|
+
checked(makepri, ["new", "/pr", stage, "/cf", priConfig, "/of", path.join(stage, "resources.pri"), "/o"], "makepri new");
|
|
78
|
+
if (!existsSync(path.join(stage, "resources.pri"))) fail("resources.pri was not produced; the taskbar icon would be blank");
|
|
79
|
+
rmSync(msix, { force: true });
|
|
80
|
+
checked(makeappx, ["pack", "/o", "/d", stage, "/nv", "/p", msix], "makeappx pack");
|
|
81
|
+
if (!existsSync(msix) || statSync(msix).size === 0) fail(`makeappx produced no package: ${msix}`);
|
|
82
|
+
const roundTrip = path.join(work, "roundtrip");
|
|
83
|
+
checked(makeappx, ["unpack", "/p", msix, "/d", roundTrip, "/o", "/nv"], "makeappx unpack round-trip");
|
|
84
|
+
const missing = expectedPackageFiles(plan).filter((rel) => !existsSync(path.join(roundTrip, ...rel.split("/"))));
|
|
85
|
+
if (missing.length) fail(`round-trip unpack of ${plan.package} is missing: ${missing.join(", ")}`);
|
|
86
|
+
const externalMissing = [...plan.files.map((f) => f.to), ...scaledNames(plan)].filter((n) => !existsSync(path.join(external, n)));
|
|
87
|
+
if (externalMissing.length) fail(`external Assets lacks files the PRI indexes: ${externalMissing.join(", ")}`);
|
|
88
|
+
const dump = path.join(work, "pri-dump.xml");
|
|
89
|
+
checked(makepri, ["dump", "/if", path.join(roundTrip, "resources.pri"), "/of", dump, "/o"], "makepri dump");
|
|
90
|
+
const dumpXml = readFileSync(dump, "utf8");
|
|
91
|
+
const unindexed = expectedPriUris(plan, stamped.name).filter((uri) => !dumpXml.includes(uri));
|
|
92
|
+
if (unindexed.length) fail(`packaged resources.pri does not index: ${unindexed.join(", ")}`);
|
|
93
|
+
// The one provisioned signer; it verifies after signing and exits non-zero on failure. No receipt file: nothing is written but the package.
|
|
94
|
+
(deps.sign ?? ((file) => checked(process.execPath, [SIGN_WINDOWS, file], "sign-windows")))(msix);
|
|
95
|
+
log(`signed ${plan.package}`);
|
|
96
|
+
} catch (error) {
|
|
97
|
+
rmSync(msix, { force: true }); // never leave an unchecked or unsigned package that a later run would take as present
|
|
98
|
+
throw error;
|
|
99
|
+
} finally { rmSync(work, { recursive: true, force: true }); }
|
|
100
|
+
const files = [`${plan.out}/${plan.package}`, ...[...plan.files.map((f) => f.to), ...scaledNames(plan)].map((n) => `${plan.out}/external-assets/Assets/${n}`)];
|
|
101
|
+
return { skipped: null, steps, out: plan.out, package: plan.package, files };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function parseArgs(argv) {
|
|
105
|
+
const opts = { config: null, json: false, dryRun: false };
|
|
106
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
107
|
+
if (argv[i] === "--config") { opts.config = argv[i + 1]; i += 1; if (!opts.config || opts.config.startsWith("--")) throw new Error("--config needs a value"); }
|
|
108
|
+
else if (argv[i] === "--json") opts.json = true;
|
|
109
|
+
else if (argv[i] === "--dry-run") opts.dryRun = true;
|
|
110
|
+
else throw new Error(`unknown windows-identity argument: ${argv[i]}`);
|
|
111
|
+
}
|
|
112
|
+
if (!opts.config) throw new Error("--config <file> is required");
|
|
113
|
+
return opts;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export async function main(argv = process.argv.slice(2), { env = process.env, platform = process.platform, spawnSyncFn = spawnSync, loadConfig = loadAppConfig, requireDoor, deps = {}, out = (s) => console.log(s) } = {}) {
|
|
117
|
+
const opts = parseArgs(argv);
|
|
118
|
+
const appRoot = path.dirname(path.resolve(opts.config));
|
|
119
|
+
// The door first (a .mjs config is code), then the platform: a non-Windows host builds nothing and says so.
|
|
120
|
+
const refusal = webBuildDoorRefusal({ appRoot, env, spawnSyncFn, ...(requireDoor ? { requireDoor } : {}) });
|
|
121
|
+
if (refusal) fail(refusal.replace("right-release web-build", "windows-identity"));
|
|
122
|
+
const result = (r) => { if (opts.json) out(JSON.stringify(r)); return 0; };
|
|
123
|
+
if (platform !== "win32") return result({ skipped: "not-windows", files: [] });
|
|
124
|
+
const { config } = await loadConfig(opts.config);
|
|
125
|
+
const { problems, plan } = planWindowsIdentity(config);
|
|
126
|
+
if (!plan) fail(problems.join("; "));
|
|
127
|
+
if (opts.dryRun) return result({ skipped: "dry-run", steps: windowsIdentitySteps(plan), out: plan.out, package: plan.package, files: [] });
|
|
128
|
+
return result(buildWindowsIdentity({ plan, appRoot }, { platform, env, ...deps }));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) {
|
|
132
|
+
main().then((code) => { process.exitCode = code; }, (error) => {
|
|
133
|
+
console.error(`windows-identity: ${error.code ? `[${error.code}] ` : ""}${error.message}`);
|
|
134
|
+
process.exitCode = 1;
|
|
135
|
+
});
|
|
136
|
+
}
|