fix: harden upgrade publication and refresh

This commit is contained in:
golem
2026-08-25 00:16:20 -06:00
parent d29dc86383
commit eb7c2c18b3
8 changed files with 267 additions and 32 deletions
+17 -3
View File
@@ -1,5 +1,6 @@
import { mkdir, readFile, writeFile } from "node:fs/promises";
import { dirname, resolve } from "node:path";
import { randomUUID } from "node:crypto";
import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
const repositoryRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
@@ -11,6 +12,7 @@ const secretMarker = /(?:private[_ -]?key|mnemonic|secret|password|credential|ap
export async function publishManifest({
activePath = resolve(repositoryRoot, "deployments/active.json"),
outputPath = resolve(repositoryRoot, "web/public/deployment.json"),
io,
} = {}) {
let contents;
try { contents = await readFile(activePath, "utf8"); } catch { throw new Error(`active manifest is missing at ${activePath}`); }
@@ -18,10 +20,22 @@ export async function publishManifest({
try { manifest = JSON.parse(contents); } catch { throw new Error("active manifest contains invalid JSON"); }
validatePublicManifest(manifest);
await mkdir(dirname(outputPath), { recursive: true });
await writeFile(outputPath, `${JSON.stringify(manifest, null, 2)}\n`);
await atomicPublish(outputPath, `${JSON.stringify(manifest, null, 2)}\n`, io);
return manifest;
}
async function atomicPublish(path, contents, io = {}) {
const temporary = join(dirname(path), `.${randomUUID()}.json`);
const operations = { writeFile, rename, rm, ...io };
try {
await operations.writeFile(temporary, contents, { mode: 0o600 });
await operations.rename(temporary, path);
} catch (error) {
await operations.rm(temporary, { force: true }).catch(() => {});
throw error;
}
}
export function validatePublicManifest(manifest) {
if (!isRecord(manifest)) throw new Error("manifest must be an object");
for (const field of requiredFields) if (!Object.hasOwn(manifest, field)) throw new Error(`manifest is missing required field ${field}`);