import { readFileSync, existsSync, unlinkSync } from "node:fs"; import { execSync } from "node:child_process"; import path from "node:path"; const ROOT = path.resolve(import.meta.dirname, ".."); const PID_FILE = path.join(ROOT, ".e2e-server-pid"); const MOCK_LLM_PORT = Number(process.env.MOCK_LLM_PORT) || 3600; const PREVIEW_PORT = Number(process.env.NITRO_PORT) || 3400; /** * 杀掉占用指定端口的进程,确保即使 setup 崩溃也能清理遗留进程。 */ function killPortOccupants(port: number) { try { execSync(`fuser -k ${port}/tcp 2>/dev/null || true`, { stdio: "pipe" }); } catch { try { const out = execSync(`lsof -t -i :${port} 2>/dev/null || true`, { stdio: "pipe", }).toString().trim(); if (out) { for (const pid of out.split("\n")) { try { process.kill(Number(pid), "SIGKILL"); } catch { /* 已退出 */ } } } } catch { /* 忽略 */ } } } async function main() { // 1. 通过 PID 文件优雅停止 preview server if (existsSync(PID_FILE)) { const pid = parseInt(readFileSync(PID_FILE, "utf-8").trim(), 10); if (pid) { try { process.kill(pid, "SIGTERM"); console.log(`[global-teardown] Stopped preview server (PID: ${pid})`); } catch { // 进程可能已退出 } } unlinkSync(PID_FILE); } // 2. 兜底清理:杀掉占用端口的残留进程(mock LLM + preview server) // 防止 setup 崩溃后 mock LLM 进程遗留导致下次 EADDRINUSE console.log(`[global-teardown] Cleaning up ports: ${MOCK_LLM_PORT}, ${PREVIEW_PORT}`); killPortOccupants(MOCK_LLM_PORT); killPortOccupants(PREVIEW_PORT); console.log("[global-teardown] Done"); } export default main;