| 1 | import { execFile, spawn } from "node:child_process"; |
| 2 | import { watch } from "node:fs"; |
| 3 | import { promisify } from "node:util"; |
| 4 | |
| 5 | const host = process.env.STUDIO_DEPLOY_HOST ?? "root@127.0.0.1"; |
| 6 | const ssh = ["-p", process.env.STUDIO_DEPLOY_PORT ?? "2222", "-o", "BatchMode=yes", "-o", "ConnectTimeout=8"]; |
| 7 | const dir = "/root/studio-dashboard-dev"; |
| 8 | const run = promisify(execFile); |
| 9 | const quote = (value: string) => "'" + value.replaceAll("'", "'\\''") + "'"; |
| 10 | const restart = `import os, shlex, subprocess |
| 11 | variables = dict(item.split("=", 1) for item in shlex.split(subprocess.check_output(["systemctl", "show", "-P", "Environment", "studio-dashboard"], text=True))) |
| 12 | for key in ["STUDIO_INDEX_POOL", "STUDIO_YT_STATE", "STUDIO_FILES_WRITABLE"]: variables.pop(key, None) |
| 13 | variables.update(PORT="7070", STUDIO_METRICS="follow", STUDIO_DATA_DIR="${dir}/data") |
| 14 | subprocess.run(["systemctl", "stop", "studio-dashboard-dev"], check=False) |
| 15 | subprocess.run(["systemd-run", "--unit=studio-dashboard-dev", "--collect", "--property=WorkingDirectory=${dir}/result/lib/home-dashboard", *["--setenv="+key+"="+value for key,value in variables.items()], "${dir}/result/bin/home-dashboard"], check=True)`; |
| 16 | let pending = false; |
| 17 | let rebuilding = false; |
| 18 | async function rebuild() { |
| 19 | pending = true; |
| 20 | if (rebuilding) return; |
| 21 | rebuilding = true; |
| 22 | try { |
| 23 | while (pending) { |
| 24 | pending = false; |
| 25 | await run("rsync", ["-a", "--delete", ...[".jj", ".git", "node_modules", "target", "dist", ".cache", "data", "result"].map(name => "--exclude=" + name), "-e", ["ssh", ...ssh].join(" "), "../", `${host}:${dir}/`]); |
| 26 | await run("ssh", [...ssh, host, `cd ${dir} && nix --extra-experimental-features 'nix-command flakes' build path:.#dashboard --cores 2 --max-jobs 1 -o result && python3 -c ${quote(restart)}`], { maxBuffer: 16 * 1024 * 1024 }); |
| 27 | console.log("Rust dashboard ready"); |
| 28 | } |
| 29 | } finally { rebuilding = false; } |
| 30 | } |
| 31 | await rebuild(); |
| 32 | watch("src", { recursive: true }, () => rebuild().catch(console.error)); |
| 33 | const tunnel = spawn("ssh", [...ssh, "-N", "-o", "ExitOnForwardFailure=yes", "-L", "7070:127.0.0.1:7070", host], { stdio: "inherit" }); |
| 34 | tunnel.on("exit", code => process.exit(code ?? 1)); |
| 35 | for (const signal of ["SIGINT", "SIGTERM"] as const) process.on(signal, () => { |
| 36 | run("ssh", [...ssh, host, "systemctl stop studio-dashboard-dev"]).finally(() => tunnel.kill()); |
| 37 | }); |