Diffinery

codician/diffinerypublic

Open-source, agent-first GitHub and GitHub Actions alternative that runs on your own Cloudflare account: git hosting, pull requests, code review and drop-in GitHub Actions YAML on Workers, Durable Objects, R2 and Containers.

61 lines · 3.7 KB · executableRaw History
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).
15import { readFileSync, writeFileSync } from "node:fs";
16import { dirname, join } from "node:path";
17import { fileURLToPath } from "node:url";
18import { parseArgs } from "node:util";
19import { parseJsonc } from "./jsonc.mjs";
20
21const { 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" } } });
22if (!values.d1) throw new Error("--d1 <database id> is required");
23
24const appDir = join(dirname(fileURLToPath(import.meta.url)), "..", "apps", "diffinery");
25const cfg = parseJsonc(readFileSync(join(appDir, "wrangler.jsonc"), "utf8"));
26delete cfg.env;
27delete cfg.$schema;
28cfg.d1_databases[0].database_id = values.d1;
29if (values.team) cfg.vars.ACCESS_TEAM_DOMAIN = values.team;
30if (values.aud) cfg.vars.ACCESS_AUD = values.aud;
31if (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}
39if (values["mail-from"]) cfg.vars.MAIL_FROM = values["mail-from"];
40if (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.
49if (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.
59if (process.env.TURNSTILE_SITE_KEY && !cfg.vars.TURNSTILE_SITE_KEY) cfg.vars.TURNSTILE_SITE_KEY = process.env.TURNSTILE_SITE_KEY;
60writeFileSync(join(appDir, "wrangler.deploy.jsonc"), `// Generated by scripts/render-config.mjs; do not edit.\n${JSON.stringify(cfg, null, 2)}\n`);
61console.log(`wrote ${join(appDir, "wrangler.deploy.jsonc")}`);