bzl

self-hosted ephemeral community engine
Log | Files | Refs | README | LICENSE

multi-instance-update.js (3366B)


      1 const fs = require("fs");
      2 const path = require("path");
      3 const { spawnSync } = require("child_process");
      4 
      5 const ROOT = path.join(__dirname, "..");
      6 
      7 function log(msg) {
      8   console.log(`[multi-update] ${msg}`);
      9 }
     10 
     11 function fail(msg) {
     12   console.error(`[multi-update] ERROR: ${msg}`);
     13   process.exit(1);
     14 }
     15 
     16 function parseArgs() {
     17   const args = process.argv.slice(2);
     18   const out = {};
     19   for (const raw of args) {
     20     const a = String(raw || "");
     21     if (!a.startsWith("--")) continue;
     22     const eq = a.indexOf("=");
     23     if (eq > -1) {
     24       out[a.slice(2, eq)] = a.slice(eq + 1);
     25       continue;
     26     }
     27     out[a.slice(2)] = "1";
     28   }
     29   return out;
     30 }
     31 
     32 function run(cmd, args, cwd = ROOT, { allowFail = false } = {}) {
     33   const result = spawnSync(cmd, args, { cwd, stdio: "inherit", shell: false });
     34   if (result.error) fail(`${cmd} failed to start: ${result.error?.message || result.error}`);
     35   const code = result.status || 0;
     36   if (code !== 0 && !allowFail) fail(`${cmd} ${args.join(" ")} failed with exit code ${code}`);
     37   return code;
     38 }
     39 
     40 function main() {
     41   const args = parseArgs();
     42   const remote = String(args.remote || "origin").trim();
     43   const branch = String(args.branch || "main").trim();
     44   const configPath = args.config
     45     ? path.resolve(process.cwd(), String(args.config))
     46     : path.join(ROOT, "multi_instance", "instances.json");
     47   const composePath = args.compose
     48     ? path.resolve(process.cwd(), String(args.compose))
     49     : path.join(path.dirname(configPath), "docker-compose.yml");
     50 
     51   const skipGit = args["skip-git"] === "1";
     52   const skipBuild = args["skip-build"] === "1";
     53   const routeDns = args["route-dns"] === "1";
     54   const dryRun = args["dry-run"] === "1";
     55 
     56   if (!skipGit) {
     57     log(`Pulling latest source-of-truth from ${remote}/${branch}...`);
     58     run("git", ["fetch", remote], ROOT);
     59     run("git", ["checkout", branch], ROOT);
     60     run("git", ["pull", "--ff-only", remote, branch], ROOT);
     61   } else {
     62     log("Skipping git fetch/pull (--skip-git).");
     63   }
     64 
     65   if (!fs.existsSync(configPath)) {
     66     fail(`Missing config: ${configPath}\nRun: node scripts/multi-instance-init.js --config=${configPath}`);
     67   }
     68 
     69   log("Regenerating multi-instance compose/env files...");
     70   const initArgs = [`--config=${configPath}`];
     71   if (routeDns) initArgs.push("--route-dns");
     72   run(process.execPath, [path.join(ROOT, "scripts", "multi-instance-init.js"), ...initArgs], ROOT);
     73 
     74   if (!fs.existsSync(composePath)) fail(`Expected compose file was not generated: ${composePath}`);
     75 
     76   const composeRef = composePath.replace(/\\/g, "/");
     77   const upArgs = ["compose", "-f", composeRef, "up", "-d", "--remove-orphans"];
     78   if (!skipBuild) upArgs.push("--build");
     79 
     80   if (dryRun) {
     81     log("Dry run mode enabled (--dry-run). No docker compose command executed.");
     82     console.log("");
     83     console.log("Would run:");
     84     console.log(`docker ${upArgs.join(" ")}`);
     85     return;
     86   }
     87 
     88   log("Updating all instances...");
     89   run("docker", upArgs, ROOT);
     90 
     91   log("Container status:");
     92   run("docker", ["compose", "-f", composeRef, "ps"], ROOT, { allowFail: true });
     93 
     94   console.log("");
     95   console.log("Done.");
     96   console.log(`- Updated compose stack: ${path.relative(ROOT, composePath).replace(/\\/g, "/")}`);
     97   console.log("- Verify health for each hostname and /api/health endpoint.");
     98   console.log("- If cloudflared ingress changed, restart tunnel service/process.");
     99 }
    100 
    101 main();