47 lines
2.5 KiB
JavaScript
47 lines
2.5 KiB
JavaScript
import { createHash, createPrivateKey, createPublicKey, sign, verify } from "node:crypto";
|
|
import { readFile, stat, writeFile } from "node:fs/promises";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
const pkg = JSON.parse(await readFile(path.join(root, "package.json"), "utf8"));
|
|
const privatePath = path.resolve(
|
|
process.env.FORGEFLOW_UPDATE_SIGNING_PRIVATE_KEY ||
|
|
path.join(process.env.APPDATA || path.join(os.homedir(), "AppData", "Roaming"), "forgeflow", "release-signing-private.pem"),
|
|
);
|
|
const publicPath = path.join(root, "build", "update-signing-public.pem");
|
|
const privateKey = createPrivateKey(await readFile(privatePath).catch((error) => {
|
|
if (error.code === "ENOENT") throw new Error(`ForgeFlow update signing key is missing. Run npm run signing:setup once. Expected: ${privatePath}`);
|
|
throw error;
|
|
}));
|
|
const publicKey = createPublicKey(await readFile(publicPath));
|
|
if (!publicKey.equals(createPublicKey(privateKey))) throw new Error("The release private key does not match the public key embedded in ForgeFlow.");
|
|
|
|
const provenance = JSON.parse(await readFile(path.join(root, "dist", `ForgeFlow-${pkg.version}-provenance.json`), "utf8"));
|
|
const artifacts = [];
|
|
for (const kind of ["Setup", "Portable"]) {
|
|
const name = `ForgeFlow-${kind}-${pkg.version}-win-x64.exe`;
|
|
const filePath = path.join(root, "dist", name);
|
|
const bytes = await readFile(filePath);
|
|
artifacts.push({ name, bytes: (await stat(filePath)).size, sha256: createHash("sha256").update(bytes).digest("hex") });
|
|
}
|
|
const keyId = createHash("sha256").update(publicKey.export({ type: "spki", format: "der" })).digest("hex");
|
|
const manifest = {
|
|
schemaVersion: 1,
|
|
product: "ForgeFlow",
|
|
version: pkg.version,
|
|
tag: `v${pkg.version}`,
|
|
commit: provenance.commit,
|
|
buildId: provenance.buildId,
|
|
signature: { algorithm: "Ed25519", keyId: `SHA256:${keyId}` },
|
|
artifacts,
|
|
};
|
|
const manifestBytes = Buffer.from(`${JSON.stringify(manifest, null, 2)}\n`, "utf8");
|
|
const signature = sign(null, manifestBytes, privateKey);
|
|
if (!verify(null, manifestBytes, publicKey, signature)) throw new Error("The generated release signature did not verify.");
|
|
const manifestName = `ForgeFlow-${pkg.version}-release-manifest.json`;
|
|
await writeFile(path.join(root, "dist", manifestName), manifestBytes, { mode: 0o644 });
|
|
await writeFile(path.join(root, "dist", `${manifestName}.sig`), `${signature.toString("base64")}\n`, { mode: 0o644 });
|
|
console.log(`${manifestName}: signed with SHA256:${keyId}`);
|