Files
waggle-os/app/scripts/apply-signing-config.mjs
Oleg Maslov 0c3e2ead3b
Some checks failed
Installer Smoke / installer-smoke (push) Has been cancelled
moving
2026-09-02 10:10:29 +02:00

131 lines
4.5 KiB
JavaScript

#!/usr/bin/env node
/**
* apply-signing-config.mjs — write the captured Windows code-signing thumbprint
* into app/src-tauri/tauri.build-override.conf.json.
*
* Read by `npm run tauri:sign:pilot:win:apply`. Idempotent: re-running with the
* same thumbprint produces an identical file. Updating the cert (rotation) is
* handled by re-running the upstream cert-gen script + this CLI.
*
* Pure logic lives in `signing-config.ts`; this is the thin file-I/O wrapper.
*
* Reference: docs/code-signing-pilot-and-launch.md §1.1
*/
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
import { resolve, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
// The pure helpers below mirror app/scripts/signing-config.ts so this CLI has
// zero TS-loader dependency at runtime. The .ts version is the canonical
// implementation tested by signing-config.test.ts (19 cases covering parse,
// merge, idempotency, immutability). Keep the two implementations in lockstep:
// any change to parseThumbprintString or addWindowsSigningToOverride below
// MUST be mirrored in signing-config.ts and vice versa.
const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url));
const APP_DIR = resolve(SCRIPT_DIR, '..');
const REPO_ROOT = resolve(APP_DIR, '..');
const OVERRIDE_PATH = resolve(
APP_DIR,
'src-tauri',
'tauri.build-override.conf.json',
);
const THUMBPRINT_PATH = resolve(APP_DIR, 'src-tauri', '.thumbprint.txt');
const DEFAULT_DIGEST_ALGORITHM = 'sha256';
const DEFAULT_TIMESTAMP_URL = 'http://timestamp.digicert.com';
const THUMBPRINT_LENGTH = 40;
const HEX_PATTERN = /^[0-9A-F]+$/;
function parseThumbprintString(raw) {
if (!raw || raw.trim().length === 0) {
throw new Error('Thumbprint is empty — cert generation may have failed.');
}
const compact = raw.replace(/\s+/g, '').toUpperCase();
if (compact.length !== THUMBPRINT_LENGTH || !HEX_PATTERN.test(compact)) {
throw new Error(
`Thumbprint must be 40 hex characters; got ${compact.length} chars (sample: "${compact.slice(0, 16)}...").`,
);
}
return compact;
}
function addWindowsSigningToOverride(config, thumbprint, options = {}) {
const normalisedThumbprint = parseThumbprintString(thumbprint);
const digestAlgorithm = options.digestAlgorithm ?? DEFAULT_DIGEST_ALGORITHM;
const timestampUrl = options.timestampUrl ?? DEFAULT_TIMESTAMP_URL;
const existingBundle = config.bundle ?? {};
const existingWindows = existingBundle.windows ?? {};
return {
...config,
bundle: {
...existingBundle,
windows: {
...existingWindows,
certificateThumbprint: normalisedThumbprint,
digestAlgorithm,
timestampUrl,
},
},
};
}
// ─── Main ───────────────────────────────────────────────────────────────────
function main() {
if (!existsSync(THUMBPRINT_PATH)) {
console.error(
`[apply-signing-config] thumbprint file missing: ${THUMBPRINT_PATH}`,
);
console.error(
'[apply-signing-config] Run `npm run tauri:sign:pilot:win:setup` (or app/scripts/sign-windows-pilot.ps1 -Mode Setup) first.',
);
process.exit(1);
}
if (!existsSync(OVERRIDE_PATH)) {
console.error(
`[apply-signing-config] override config missing: ${OVERRIDE_PATH}`,
);
process.exit(1);
}
const rawThumbprint = readFileSync(THUMBPRINT_PATH, 'utf8');
const overrideRaw = readFileSync(OVERRIDE_PATH, 'utf8');
let override;
try {
override = JSON.parse(overrideRaw);
} catch (err) {
console.error(
`[apply-signing-config] failed to parse ${OVERRIDE_PATH}: ${err instanceof Error ? err.message : String(err)}`,
);
process.exit(1);
}
let updated;
try {
updated = addWindowsSigningToOverride(override, rawThumbprint);
} catch (err) {
console.error(
`[apply-signing-config] failed: ${err instanceof Error ? err.message : String(err)}`,
);
process.exit(1);
}
// Format with 2-space indent + trailing newline (matches existing JSON files
// in the repo). Idempotent: same input → same output bytes.
const serialised = JSON.stringify(updated, null, 2) + '\n';
writeFileSync(OVERRIDE_PATH, serialised, 'utf8');
const relativePath = OVERRIDE_PATH.replace(REPO_ROOT, '').replace(/^\\/, '');
console.log(
`[apply-signing-config] wrote thumbprint ${updated.bundle.windows.certificateThumbprint.slice(0, 8)}... to ${relativePath}`,
);
}
main();