// Runs berry, and runs it again when it exits to restart, or crashes. import { fromFileUrl } from "@std/path"; import { denoExecutable } from "./deno.ts"; /** The exit code berry restarts with. */ export const RESTART = 75; if (import.meta.main) { const main = fromFileUrl(new URL("./main.ts", import.meta.url)); let stopping = false; let child: Deno.ChildProcess | undefined; // the terminal's ^C reaches berry too; either way, berry stops and then so does this for (const signal of ["SIGINT", "SIGTERM"] as const) { Deno.addSignalListener(signal, () => { stopping = true; try { child?.kill(signal); } catch { // already gone } }); } let backoff = 1_000; while (true) { const started = Date.now(); child = new Deno.Command(denoExecutable(), { args: ["run", "-A", main] }).spawn(); const { code } = await child.status; if (stopping || code === 0) Deno.exit(code); if (code === RESTART) { console.log("[berry] restarting"); backoff = 1_000; continue; } // a long run means the crash is news, not the same broken code again if (Date.now() - started > 60_000) backoff = 1_000; console.error(`[berry] exited with ${code}; starting again in ${backoff / 1000}s`); await new Promise((resolve) => setTimeout(resolve, backoff)); backoff = Math.min(backoff * 2, 30_000); } }