import { spawnSync } from "node:child_process"; import path from "node:path"; import { fileURLToPath } from "node:url"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const repoRoot = path.resolve(__dirname, ".."); const args = new Set(process.argv.slice(2)); if (args.has("--help") || args.has("-h")) { console.log(`Reset production runtime data for the container stack. Usage: npm run reset:prod npm run reset:prod -- --keep-down npm run reset:prod -- --tool=docker npm run reset:prod -- --tool=podman Options: --keep-down Do not restart the stack after reset. --tool= Force compose tool: docker or podman. --help, -h Show this help output. `); process.exit(0); } const forcedToolArg = [...args].find((arg) => arg.startsWith("--tool=")); const forcedTool = forcedToolArg ? forcedToolArg.slice("--tool=".length) : null; const keepDown = args.has("--keep-down"); function run(command, commandArgs, options = {}) { const result = spawnSync(command, commandArgs, { cwd: repoRoot, stdio: options.capture ? ["ignore", "pipe", "pipe"] : "inherit", encoding: "utf8" }); if (options.allowFailure) { return result; } if (result.status !== 0) { const stderr = result.stderr?.trim(); throw new Error( stderr ? `${command} ${commandArgs.join(" ")} failed: ${stderr}` : `${command} ${commandArgs.join(" ")} failed with exit code ${result.status ?? "unknown"}` ); } return result; } function composeCommandFor(tool) { if (tool === "docker") { return ["docker", ["compose"]]; } if (tool === "podman") { return ["podman", ["compose"]]; } throw new Error(`Unsupported compose tool: ${tool}`); } function detectComposeTool() { const candidates = forcedTool ? [forcedTool] : ["docker", "podman"]; for (const candidate of candidates) { const [command, baseArgs] = composeCommandFor(candidate); const probe = run(command, [...baseArgs, "version"], { allowFailure: true, capture: true }); if (probe.status === 0) { return { tool: candidate, command, baseArgs }; } } throw new Error("Could not find a working compose tool. Install Docker Compose or Podman Compose, or pass --tool=docker|podman."); } const composeTool = detectComposeTool(); const composeArgs = [ ...composeTool.baseArgs, "-f", "docker-compose.yml", "-f", "docker-compose.prod.yml" ]; const services = ["api", "worker", "admin", "submission"]; function compose(commandArgs, options) { return run(composeTool.command, [...composeArgs, ...commandArgs], options); } console.log(`Using ${composeTool.tool} compose.`); const psResult = compose(["ps", "-q"], { capture: true, allowFailure: true }); const wasRunning = psResult.status === 0 && psResult.stdout.trim().length > 0; if (wasRunning) { console.log("Stopping production stack..."); compose(["down"]); } console.log("Clearing production runtime data..."); compose(["run", "--rm", "api", "npm", "run", "reset:runtime"]); if (wasRunning && !keepDown) { console.log("Restarting production stack..."); compose(["up", "-d", ...services]); } if (!wasRunning) { console.log("Production stack was not running. Data is reset and the stack remains down."); } else if (keepDown) { console.log("Production stack was stopped for reset and left down."); } else { console.log("Production stack reset and restarted."); }