Skip to content

Commit c9e3096

Browse files
committed
Capture dev-stack logs in e2e runs and raise CI heap headroom
1 parent 35cb1b4 commit c9e3096

3 files changed

Lines changed: 43 additions & 1 deletion

File tree

e2e/setup/cloud.boot.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,15 @@ export const bootCloud = async (options: CloudBootOptions): Promise<CloudBooted>
120120
options.host ?? "127.0.0.1",
121121
],
122122
cwd: cloudDir,
123-
env,
123+
// Mitigation, not a fix (the real per-request OOM growth is what the
124+
// spec-compile LRU addresses): CI runners have 7GB, well above the
125+
// default ~2GB V8 old-space ceiling, so give the dev server room
126+
// before it hits the limit and starts 500ing. Scoped to this child
127+
// only; dev-db does not need it.
128+
env: {
129+
...env,
130+
NODE_OPTIONS: `${process.env.NODE_OPTIONS ?? ""} --max-old-space-size=3072`.trim(),
131+
},
124132
logFile: options.logFile,
125133
},
126134
],

e2e/setup/cloud.globalsetup.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,35 @@
33
// (cloud.boot.ts — emulated WorkOS + Autumn + the app's real dev stack, the
44
// same recipe the dev CLI uses). Set E2E_CLOUD_URL to attach to a running
55
// stack instead.
6+
import { mkdirSync } from "node:fs";
7+
import { resolve } from "node:path";
8+
69
import { claimAndBoot } from "../src/ports";
710
import { E2E_COOKIE_PASSWORD, E2E_WORKOS_CLIENT_ID } from "../targets/cloud";
811
import { waitForHttp } from "./boot";
912
import { bootCloud } from "./cloud.boot";
1013
import { bootMotel, motelExporterEnv } from "./motel";
14+
import { RUNS_DIR } from "../src/scenario";
15+
16+
// dev-db + vite dev stdout/stderr, swept into the failure-only artifact
17+
// upload (CI uploads e2e/runs/**): see .github/workflows/ci.yml. A
18+
// mid-shard OOM abort or a 500 cascade otherwise leaves no trace: boot.ts
19+
// defaults to stdio "ignore" unless E2E_VERBOSE=1. Skip the file when
20+
// E2E_VERBOSE is set so local `E2E_VERBOSE=1` keeps its inherited,
21+
// live-in-terminal output (boot.ts prioritizes logFile over E2E_VERBOSE, so
22+
// passing both would silently swallow the verbose stream into the file).
23+
const bootLogFile = process.env.E2E_VERBOSE
24+
? undefined
25+
: resolve(RUNS_DIR, "cloud", ".server", "boot.log");
1126

1227
export default async function setup(): Promise<(() => Promise<void>) | void> {
1328
if (process.env.E2E_CLOUD_URL) {
1429
await waitForHttp(process.env.E2E_CLOUD_URL);
1530
return;
1631
}
1732

33+
if (bootLogFile) mkdirSync(resolve(bootLogFile, ".."), { recursive: true });
34+
1835
// Suite-owned trace store — every run captures distributed traces. Booted
1936
// once outside the retry: it binds its own OS-assigned port (not a claimed
2037
// one), so an EADDRINUSE on the claimed block doesn't implicate it.
@@ -50,6 +67,7 @@ export default async function setup(): Promise<(() => Promise<void>) | void> {
5067
workosClientId: E2E_WORKOS_CLIENT_ID,
5168
cookiePassword: E2E_COOKIE_PASSWORD,
5269
publicUrl,
70+
logFile: bootLogFile,
5371
// Server + browser spans → the suite's motel. The app's exporter is
5472
// endpoint-agnostic, so the same layer that ships prod traces to
5573
// Axiom ships e2e traces to the suite store — "why was that page

e2e/setup/selfhost.globalsetup.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,32 @@
22
// (src/ports.ts), then run the shared boot recipe (selfhost.boot.ts — the
33
// same one the dev CLI uses). Set E2E_SELFHOST_URL to attach to a running
44
// instance (with E2E_SELFHOST_ADMIN_EMAIL/PASSWORD matching it).
5+
import { mkdirSync } from "node:fs";
6+
import { resolve } from "node:path";
7+
58
import { claimAndBoot } from "../src/ports";
69
import { SELFHOST_ADMIN } from "../targets/selfhost";
710
import { waitForHttp } from "./boot";
811
import { bootSelfhost } from "./selfhost.boot";
12+
import { RUNS_DIR } from "../src/scenario";
13+
14+
// vite dev stdout/stderr, swept into the failure-only artifact upload (CI
15+
// uploads e2e/runs/**): see .github/workflows/ci.yml and cloud.globalsetup.ts
16+
// for the same pattern. Skip when E2E_VERBOSE=1 so local verbose runs keep
17+
// their inherited, live-in-terminal output (boot.ts prioritizes logFile over
18+
// E2E_VERBOSE).
19+
const bootLogFile = process.env.E2E_VERBOSE
20+
? undefined
21+
: resolve(RUNS_DIR, "selfhost", ".server", "boot.log");
922

1023
export default async function setup(): Promise<(() => Promise<void>) | void> {
1124
if (process.env.E2E_SELFHOST_URL) {
1225
await waitForHttp(process.env.E2E_SELFHOST_URL);
1326
return;
1427
}
1528

29+
if (bootLogFile) mkdirSync(resolve(bootLogFile, ".."), { recursive: true });
30+
1631
// Claim a free port (preferred block first, walk forward past squatters),
1732
// boot, and retry on EADDRINUSE (a Linux-CI ephemeral socket can grab a
1833
// claimed port between probe and bind). The claimed port is published via env
@@ -28,6 +43,7 @@ export default async function setup(): Promise<(() => Promise<void>) | void> {
2843
port,
2944
webBaseUrl: `http://localhost:${port}`,
3045
admin: SELFHOST_ADMIN,
46+
logFile: bootLogFile,
3147
});
3248
return { teardown: procs.teardown, value: procs };
3349
},

0 commit comments

Comments
 (0)