MCPcopy Create free account
hub / github.com/UsefulSoftwareCo/executor / bootProcesses

Function bootProcesses

e2e/setup/boot.ts:14–81  ·  view source on GitHub ↗
(
  procs: ReadonlyArray<{
    readonly cmd: string;
    readonly args: ReadonlyArray<string>;
    readonly cwd: string;
    readonly env?: Record<string, string | undefined>;
    /** Append stdout+stderr here (long-lived boots need inspectable logs). */
    readonly logFile?: string;
  }>,
  options: { readonly label: string },
)

Source from the content-addressed store, hash-verified

12}
13
14export const bootProcesses = (
15 procs: ReadonlyArray<{
16 readonly cmd: string;
17 readonly args: ReadonlyArray<string>;
18 readonly cwd: string;
19 readonly env?: Record<string, string | undefined>;
20 /** Append stdout+stderr here (long-lived boots need inspectable logs). */
21 readonly logFile?: string;
22 }>,
23 options: { readonly label: string },
24): BootedProcesses => {
25 const children: ChildProcess[] = [];
26 let tearingDown = false;
27 for (const proc of procs) {
28 const log = proc.logFile ? openSync(proc.logFile, "a") : undefined;
29 const child = spawn(proc.cmd, [...proc.args], {
30 cwd: proc.cwd,
31 env: { ...process.env, ...proc.env },
32 stdio:
33 log !== undefined ? ["ignore", log, log] : process.env.E2E_VERBOSE ? "inherit" : "ignore",
34 // Own process group, so teardown can signal the whole tree — `bunx vite`
35 // is a wrapper whose actual server child would otherwise outlive the
36 // kill and squat the port into the NEXT invocation's waitForHttp.
37 detached: true,
38 });
39 child.on("exit", (code) => {
40 if (code !== 0 && code !== null && !tearingDown) {
41 console.error(`[e2e:${options.label}] ${proc.cmd} exited with ${code}`);
42 }
43 });
44 children.push(child);
45 }
46
47 // Signal the process GROUP (negative pid); fall back to the direct child
48 // when the group is already gone.
49 const signalTree = (child: ChildProcess, signal: NodeJS.Signals) => {
50 if (child.pid === undefined || child.exitCode !== null) return;
51 try {
52 process.kill(-child.pid, signal);
53 } catch {
54 child.kill(signal);
55 }
56 };
57
58 const exited = (child: ChildProcess): Promise<void> =>
59 child.exitCode !== null || child.signalCode !== null
60 ? Promise.resolve()
61 : new Promise((resolve) => child.once("exit", () => resolve()));
62
63 return {
64 teardown: async () => {
65 tearingDown = true;
66 const allExited = Promise.all(children.map(exited));
67 for (const child of children) signalTree(child, "SIGTERM");
68 // Wait for a REAL exit (not a fixed sleep) — a lingering server would
69 // answer the next invocation's waitForHttp as a half-dead zombie.
70 const graceful = await Promise.race([
71 allExited.then(() => true),

Callers 5

bootCloudFunction · 0.90
bootCloudflareFunction · 0.90
bootMotelFunction · 0.90
bootSelfhostFunction · 0.90
boot-stack.tsFile · 0.90

Calls 4

signalTreeFunction · 0.85
errorMethod · 0.80
pushMethod · 0.80
resolveFunction · 0.50

Tested by

no test coverage detected