import { deploymentHelp } from "./args.mjs";
export function createDeploymentController(deps) {
async function runDeploy(parsed) {
const calls = [];
let configurationChanged = false;
let serviceStopped = false;
let stage = "load-config";
let config;
let logPath = "storage/logs/deploy-latest.log";
try {
if (deps.beginDeployLog) {
logPath = await deps.beginDeployLog();
}
stage = "load-config";
config = await deps.loadConfiguration({ reconfigure: parsed.reconfigure });
calls.push("load-config");
stage = "preflight";
await deps.preflight(config, parsed);
calls.push("preflight");
stage = "configure";
config = await deps.configure(config, parsed);
calls.push("configure");
stage = "check-dependencies";
await deps.checkDependencies(config, parsed);
calls.push("check-dependencies");
stage = "select-ports";
config = await deps.selectPorts(config, parsed);
calls.push("select-ports");
stage = "write-config";
await deps.writeConfiguration(config, parsed);
configurationChanged = true;
calls.push("write-config");
const running = await deps.isRunning();
if (running) {
stage = "stop-old";
await deps.stop();
serviceStopped = true;
calls.push("stop-old");
}
stage = "install";
await deps.installProject(config, parsed);
calls.push("install");
stage = "build-typescript";
await deps.buildTypeScript(config, parsed);
calls.push("build-typescript");
stage = "build-web";
await deps.buildWeb(config, parsed);
calls.push("build-web");
stage = "build-tui";
await deps.buildTui(config, parsed);
calls.push("build-tui");
stage = "verify-ports-again";
await deps.verifyPorts(config, parsed);
calls.push("verify-ports-again");
stage = "start";
await deps.start(config, parsed);
calls.push("start");
stage = "wait-for-health";
await deps.waitForHealth(config, parsed);
calls.push("wait-for-health");
stage = "mark-healthy";
await deps.markHealthy(config, parsed);
calls.push("mark-healthy");
return { ok: true, calls, configurationChanged, serviceStopped };
} catch (error) {
const failure = {
stage,
summary: error?.message ?? String(error),
error,
configurationChanged,
serviceStopped,
maintenanceWindow: serviceStopped,
oldServiceRunning: false,
logPath: error?.logPath ?? logPath,
retryCommand: "./deploy.sh deploy",
doctorCommand: "./deploy.sh doctor",
calls
};
await deps.reportFailure(failure);
const wrapped = new Error(error?.message ?? String(error));
wrapped.failure = failure;
throw wrapped;
}
}
async function runStart() {
const config = await deps.loadConfiguration({ reconfigure: false });
await deps.preflight(config, { command: "start" });
await deps.start(config, { command: "start" });
await deps.waitForHealth(config, { command: "start" });
await deps.markHealthy(config, { command: "start" });
return { ok: true };
}
async function runStop() {
await deps.stop();
return { ok: true };
}
async function runRestart() {
await deps.stop();
return runStart();
}
async function runTui(parsed) {
const config = await deps.loadConfiguration({ reconfigure: false });
await deps.ensureTuiReady(config, parsed);
await deps.checkApiForTui(config, parsed);
await deps.startTui(config, parsed);
return { ok: true };
}
return {
async run(parsed) {
switch (parsed.command) {
case "deploy":
return runDeploy(parsed);
case "start":
return runStart();
case "stop":
return runStop();
case "restart":
return runRestart();
case "status":
return deps.status();
case "logs":
return deps.logs();
case "doctor":
return deps.doctor();
case "tui":
return runTui(parsed);
case "help":
return deps.printHelp(deploymentHelp());
default:
throw new Error(`Unknown command: ${parsed.command}`);
}
}
};
}