| 1 | #!/usr/bin/env node |
| 2 | // Render apps/diffinery/wrangler.deploy.jsonc from wrangler.jsonc plus values |
| 3 | // discovered by scripts/bootstrap.sh (real D1 id, Access team domain and AUD). |
| 4 | // |
| 5 | // node scripts/render-config.mjs --d1 <uuid> [--team <x.cloudflareaccess.com>] [--aud <tag>] |
| 6 | // [--public-url https://git.example.com] [--zone example.com] [--mail-from notifications@example.com] |
| 7 | // [--admins you@example.com,ops@example.com] |
| 8 | // |
| 9 | // --public-url also points the Worker's route at that host (in --zone, by default |
| 10 | // the host's last two labels) and sends email from notifications@<host>. |
| 11 | // --admins sets BOOTSTRAP_ADMINS (comma-separated emails; bootstrap.sh passes what |
| 12 | // scripts/bootstrap-admins.sh found). Without it, wrangler.jsonc's value stays. |
| 13 | // From the environment it takes RUNNER_IMAGE (scripts/runner-image.sh) and |
| 14 | // TURNSTILE_SITE_KEY (scripts/turnstile.sh, through bootstrap). |
| 15 | import { readFileSync, writeFileSync } from "node:fs"; |
| 16 | import { dirname, join } from "node:path"; |
| 17 | import { fileURLToPath } from "node:url"; |
| 18 | import { parseArgs } from "node:util"; |
| 19 | import { parseJsonc } from "./jsonc.mjs"; |
| 20 | |
| 21 | const { values } = parseArgs({ options: { d1: { type: "string" }, team: { type: "string" }, aud: { type: "string" }, "public-url": { type: "string" }, zone: { type: "string" }, "mail-from": { type: "string" }, admins: { type: "string" } } }); |
| 22 | if (!values.d1) throw new Error("--d1 <database id> is required"); |
| 23 | |
| 24 | const appDir = join(dirname(fileURLToPath(import.meta.url)), "..", "apps", "diffinery"); |
| 25 | const cfg = parseJsonc(readFileSync(join(appDir, "wrangler.jsonc"), "utf8")); |
| 26 | delete cfg.env; |
| 27 | delete cfg.$schema; |
| 28 | cfg.d1_databases[0].database_id = values.d1; |
| 29 | if (values.team) cfg.vars.ACCESS_TEAM_DOMAIN = values.team; |
| 30 | if (values.aud) cfg.vars.ACCESS_AUD = values.aud; |
| 31 | if (values["public-url"]) { |
| 32 | const url = new URL(values["public-url"]); |
| 33 | if (url.protocol !== "https:" || url.pathname !== "/") throw new Error("--public-url must be an https origin, like https://git.example.com"); |
| 34 | cfg.vars.PUBLIC_URL = url.origin; |
| 35 | const zone = values.zone ?? url.hostname.split(".").slice(-2).join("."); |
| 36 | cfg.routes = [{ pattern: `${url.hostname}/*`, zone_name: zone }]; |
| 37 | cfg.vars.MAIL_FROM = `notifications@${url.hostname}`; |
| 38 | } |
| 39 | if (values["mail-from"]) cfg.vars.MAIL_FROM = values["mail-from"]; |
| 40 | if (values.admins !== undefined) { |
| 41 | const admins = values.admins.split(",").map((s) => s.trim()).filter(Boolean); |
| 42 | // Only verified email addresses make site administrators (src/lib/directory.ts); a login would be ignored. |
| 43 | const bad = admins.filter((a) => !/^[^\s@,]+@[^\s@,]+$/.test(a)); |
| 44 | if (bad.length) throw new Error(`--admins takes comma-separated email addresses; not an email address: ${bad.map((b) => JSON.stringify(b)).join(", ")}`); |
| 45 | cfg.vars.BOOTSTRAP_ADMINS = admins.join(","); |
| 46 | } |
| 47 | // A runner image already in the registry (scripts/runner-image.sh): containers use it |
| 48 | // instead of building the Dockerfile on every deploy. |
| 49 | if (process.env.RUNNER_IMAGE) { |
| 50 | // Named as the runner reports it (`runner.image` on a job), for /meta and the Live test. |
| 51 | cfg.vars.RUNNER_IMAGE = process.env.RUNNER_IMAGE.split("/").pop(); |
| 52 | for (const c of cfg.containers) { |
| 53 | c.image = process.env.RUNNER_IMAGE; |
| 54 | delete c.image_build_context; |
| 55 | } |
| 56 | } |
| 57 | // The site key of the Turnstile widget scripts/turnstile.sh set up for this hostname (not a |
| 58 | // secret). One set in wrangler.jsonc is the operator's own and wins; turnstile.sh then leaves it alone. |
| 59 | if (process.env.TURNSTILE_SITE_KEY && !cfg.vars.TURNSTILE_SITE_KEY) cfg.vars.TURNSTILE_SITE_KEY = process.env.TURNSTILE_SITE_KEY; |
| 60 | writeFileSync(join(appDir, "wrangler.deploy.jsonc"), `// Generated by scripts/render-config.mjs; do not edit.\n${JSON.stringify(cfg, null, 2)}\n`); |
| 61 | console.log(`wrote ${join(appDir, "wrangler.deploy.jsonc")}`); |