fix: harden upgrade publication and refresh
This commit is contained in:
@@ -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}`);
|
||||
|
||||
Reference in New Issue
Block a user