import { spawnSync } from "node:child_process"; import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { compareDeploymentCommits, isRunningContainer } from "./deployment-proof-state.mjs"; const baseUrl = process.env.PROJECTBRAIN_DEPLOY_URL ?? process.env.PROJECTBRAIN_SMOKE_URL ?? "http://127.0.0.1:1209"; const sshTarget = process.env.PROJECTBRAIN_DEPLOY_SSH_TARGET ?? "root@127.0.0.1"; const deployPath = process.env.PROJECTBRAIN_DEPLOY_PATH ?? "/mnt/pool/appdata/brain"; const sshKey = process.env.PROJECTBRAIN_DEPLOY_SSH_KEY ?? path.join(os.homedir(), ".ssh", "widefrog_unraid_deploy"); const shouldWrite = process.argv.includes("--write") || process.env.PROJECTBRAIN_DEPLOY_PROOF_WRITE === "1"; const explicitOutputArg = process.argv.find((arg) => arg.startsWith("--out=")); const explicitOutputPath = explicitOutputArg?.slice("--out=".length) || process.env.PROJECTBRAIN_DEPLOY_PROOF_OUT; const run = (command, args) => spawnSync(command, args, { encoding: "utf8", shell: false, timeout: 10_000, }); const getGitCommit = () => { const result = run("git", ["rev-parse", "--short", "HEAD"]); return result.status === 0 ? result.stdout.trim() : "unknown"; }; const createDefaultProofPath = (commit) => { const stamp = new Date().toISOString().replace(/[:.]/g, "-"); return `checkpoints/deployment-proof/projectbrain-deployment-proof-${commit}-${stamp}.md`; }; const checkHttp = async (urlPath) => { try { const response = await fetch(`${baseUrl}${urlPath}`, { signal: AbortSignal.timeout(10_000) }); const body = await response.text(); return { ok: response.ok, status: String(response.status), body: body.slice(0, 260).replace(/\s+/g, " ").trim(), }; } catch (error) { return { ok: false, status: "unreachable", body: error instanceof Error ? error.message : String(error), }; } }; const runSsh = (remoteCommand) => { if (!fs.existsSync(sshKey)) { return { ok: false, status: "missing-key", stdout: "", stderr: `SSH key not found: ${sshKey}`, }; } const result = spawnSync("ssh", [ "-i", sshKey, "-o", "BatchMode=yes", "-o", "ConnectTimeout=8", sshTarget, remoteCommand, ], { encoding: "utf8", shell: false, timeout: 20_000, }); return { ok: result.status === 0, status: String(result.status), stdout: result.stdout?.trim() ?? "", stderr: result.stderr?.trim() || result.error?.message || "", }; }; const currentCommit = getGitCommit(); const health = await checkHttp("/api/health"); const shellProof = runSsh([ `echo deployed_commit=$(git -C ${deployPath} rev-parse --short HEAD 2>/dev/null || echo unknown)`, "echo containers_begin", "docker ps --format '{{.Names}} {{.Status}} {{.Ports}}' | grep projectbrain || true", "echo containers_end", ].join(" && ")); const deployedCommitMatch = shellProof.stdout.match(/^deployed_commit=(.+)$/m); const deployedCommit = deployedCommitMatch?.[1]?.trim() ?? "unknown"; const containerBlock = shellProof.stdout.match(/containers_begin\n([\s\S]*?)\n?containers_end/)?.[1]?.trim() ?? ""; const hasFrontend = isRunningContainer(containerBlock, "projectbrain-frontend", "1209->80/tcp"); const hasApi = isRunningContainer(containerBlock, "projectbrain-api"); const hasPostgres = isRunningContainer(containerBlock, "projectbrain-postgres"); const commitFreshness = compareDeploymentCommits(currentCommit, deployedCommit); const verdict = health.ok && shellProof.ok && commitFreshness === "current" && hasFrontend && hasApi && hasPostgres ? "PASS" : "ATTENTION REQUIRED"; const proofPath = explicitOutputPath || createDefaultProofPath(currentCommit); const lines = [ "# ProjectBrain Remote Deployment Proof", "", `Generated: ${new Date().toISOString()}`, `Local commit: ${currentCommit}`, `Deployment URL: ${baseUrl}`, `SSH target: ${sshTarget}`, `Deployment path: ${deployPath}`, `Remote commit: ${deployedCommit}`, `Commit freshness: ${commitFreshness}`, `Verdict: ${verdict}`, "", "## HTTP Health", "", `- Status: ${health.status}`, `- Result: ${health.ok ? "reachable" : "not reachable"}`, `- Body: ${health.body}`, "", "## Remote Runtime", "", `- SSH result: ${shellProof.ok ? "reachable" : shellProof.status}`, `- Frontend container: ${hasFrontend ? "up" : "not proven"}`, `- API container: ${hasApi ? "up" : "not proven"}`, `- Postgres container: ${hasPostgres ? "up" : "not proven"}`, "", "```text", containerBlock || shellProof.stderr || "No remote container output captured.", "```", "", "## Operator Commands", "", "```bash", "PROJECTBRAIN_DEPLOY_PROOF_WRITE=1 pnpm run release:deployment-proof", "PROJECTBRAIN_RELEASE_DEPLOYMENT_PROOF=1 pnpm run release:bundle", "```", ].join("\n"); console.log(lines); if (shouldWrite) { fs.mkdirSync(path.dirname(proofPath), { recursive: true }); fs.writeFileSync(proofPath, `${lines}\n`, "utf8"); console.log(`\nRemote deployment proof written to ${proofPath}`); } if (verdict !== "PASS") { process.exitCode = 1; }