Add Cloudflare public cache publisher
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
#!/usr/bin/env node
|
||||
import { spawn } from "node:child_process";
|
||||
import { readFile, rm, writeFile } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join, resolve } from "node:path";
|
||||
|
||||
import {
|
||||
parsePluginRegistryDocument,
|
||||
pluginRegistryKvKey,
|
||||
} from "../dist/src/plugin_registry.js";
|
||||
import {
|
||||
parseReleaseManifestDocument,
|
||||
releaseManifestKvKey,
|
||||
} from "../dist/src/release_manifests.js";
|
||||
import {
|
||||
parsePublicSigningKeysDocument,
|
||||
publicSigningKeysKvKey,
|
||||
} from "../dist/src/signing_keys.js";
|
||||
|
||||
const DOCUMENTS = [
|
||||
{
|
||||
flag: "--signing-keys",
|
||||
label: "public signing keys",
|
||||
keyFor: publicSigningKeysKvKey,
|
||||
parse: parsePublicSigningKeysDocument,
|
||||
},
|
||||
{
|
||||
flag: "--plugin-registry",
|
||||
label: "plugin registry",
|
||||
keyFor: pluginRegistryKvKey,
|
||||
parse: parsePluginRegistryDocument,
|
||||
},
|
||||
{
|
||||
flag: "--release-manifest",
|
||||
label: "release manifest",
|
||||
keyFor: releaseManifestKvKey,
|
||||
parse: parseReleaseManifestDocument,
|
||||
},
|
||||
];
|
||||
|
||||
async function main() {
|
||||
const options = parseArgs(process.argv.slice(2));
|
||||
const publications = DOCUMENTS.flatMap((document) => {
|
||||
const path = options.documents.get(document.flag);
|
||||
return path === undefined ? [] : [{ ...document, path }];
|
||||
});
|
||||
|
||||
if (publications.length === 0) {
|
||||
throw new Error("Provide at least one public cache document path.");
|
||||
}
|
||||
|
||||
for (const publication of publications) {
|
||||
await publishDocument(options.environment, publication);
|
||||
}
|
||||
}
|
||||
|
||||
function parseArgs(args) {
|
||||
const options = {
|
||||
environment: undefined,
|
||||
documents: new Map(),
|
||||
};
|
||||
const flags = new Set(DOCUMENTS.map((document) => document.flag));
|
||||
|
||||
for (let index = 0; index < args.length; index += 1) {
|
||||
const arg = args[index];
|
||||
if (arg === "--help" || arg === "-h") {
|
||||
printUsage();
|
||||
process.exit(0);
|
||||
}
|
||||
if (arg === "--environment") {
|
||||
options.environment = requiredValue(args, index, arg);
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
if (flags.has(arg)) {
|
||||
options.documents.set(arg, requiredValue(args, index, arg));
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
throw new Error(`Unknown argument: ${arg}`);
|
||||
}
|
||||
|
||||
if (options.environment === undefined) {
|
||||
throw new Error("Missing required --environment value.");
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
function requiredValue(args, index, flag) {
|
||||
const value = args[index + 1];
|
||||
if (value === undefined || value.startsWith("--")) {
|
||||
throw new Error(`Missing value for ${flag}.`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
async function publishDocument(environment, publication) {
|
||||
const sourcePath = resolve(publication.path);
|
||||
const raw = await readFile(sourcePath, "utf8");
|
||||
const document = publication.parse(raw);
|
||||
const kvKey = publication.keyFor(environment);
|
||||
const tempPath = join(tmpdir(), `ely-public-cache-${process.pid}-${Date.now()}.json`);
|
||||
const normalized = `${JSON.stringify(document, null, 2)}\n`;
|
||||
|
||||
await writeFile(tempPath, normalized, { mode: 0o600 });
|
||||
try {
|
||||
await runWrangler([
|
||||
"kv",
|
||||
"key",
|
||||
"put",
|
||||
kvKey,
|
||||
"--binding",
|
||||
"ELY_KV",
|
||||
"--remote",
|
||||
"--path",
|
||||
tempPath,
|
||||
]);
|
||||
console.log(`Published ${publication.label} to ${kvKey}`);
|
||||
} finally {
|
||||
await rm(tempPath, { force: true });
|
||||
}
|
||||
}
|
||||
|
||||
function runWrangler(args) {
|
||||
return new Promise((resolveProcess, rejectProcess) => {
|
||||
const command = process.platform === "win32" ? "npx.cmd" : "npx";
|
||||
const child = spawn(command, ["wrangler", ...args], {
|
||||
stdio: "inherit",
|
||||
});
|
||||
|
||||
child.on("error", rejectProcess);
|
||||
child.on("exit", (code) => {
|
||||
if (code === 0) {
|
||||
resolveProcess();
|
||||
return;
|
||||
}
|
||||
rejectProcess(new Error(`wrangler exited with code ${code}`));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function printUsage() {
|
||||
console.log(`Usage:
|
||||
npm run public-cache:publish -- --environment production \\
|
||||
--signing-keys ./secure/signing-keys.json \\
|
||||
--plugin-registry ./secure/plugin-registry.json \\
|
||||
--release-manifest ./secure/release-manifest.json`);
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error(error instanceof Error ? error.message : String(error));
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user