diff --git a/frontend/Trunk.toml b/frontend/Trunk.toml index 62f1692..00943eb 100644 --- a/frontend/Trunk.toml +++ b/frontend/Trunk.toml @@ -29,8 +29,20 @@ inject_scripts = true [watch] # Paths to watch. The `build.target`'s parent folder is watched by default. watch = [] -# Paths to ignore. -ignore = ["node_modules", "style/uno.css"] +# Paths to ignore. Besides build inputs, this excludes the Playwright output +# directories: during `just e2e` the dev server watches `frontend/`, and +# Playwright continuously writes traces, screenshots, and videos into +# `test-results/` (and the HTML report into `playwright-report/`). Without these +# ignores the watcher triggers a storm of rebuilds mid-test, starving the +# browser's WASM compile/instantiate and intermittently leaving pages blank past +# the test timeout. Trunk canonicalizes these paths at startup, so the `e2e` +# recipe creates them first. +ignore = [ + "node_modules", + "style/uno.css", + "test-results", + "playwright-report", +] [serve] # The address to serve on. diff --git a/justfile b/justfile index ed5560c..0e46071 100644 --- a/justfile +++ b/justfile @@ -219,6 +219,10 @@ e2e: frontend-config frontend-e2e-typecheck source scripts/e2e-env.sh source scripts/service-graph.sh mkdir -p "$E2E_LOG_DIR" + # Pre-create Playwright's output dirs so the trunk dev server can canonicalize + # them as watch-ignore paths at startup (see frontend/Trunk.toml). Without this + # the server fails to start on a fresh checkout. + mkdir -p frontend/test-results frontend/playwright-report run_e2e() { local backend_pid="" @@ -230,11 +234,16 @@ e2e: frontend-config frontend-e2e-typecheck trap - EXIT INT TERM memory_map_stop_pid "${frontend_pid:-}" memory_map_stop_pid "${backend_pid:-}" + # The servers run behind a `direnv exec`/bash wrapper, so the tracked + # pid is the wrapper and the real server can survive on its port. Free + # the ports directly so local re-runs are not blocked by an orphan. + memory_map_free_port "${E2E_FRONTEND_PORT}" + memory_map_free_port "${E2E_BACKEND_PORT}" exit "$status" } trap cleanup_app_servers EXIT INT TERM - {{ direnv_prefix }} bash -c 'cd backend && exec cargo run --bin backend' > "$E2E_LOG_DIR/backend.log" 2>&1 & + {{ direnv_prefix }} bash -c 'cd backend && exec ../target/debug/backend' > "$E2E_LOG_DIR/backend.log" 2>&1 & backend_pid=$! if ! memory_map_wait_for_http "$E2E_BACKEND_URL/" 120 "GraphiQL"; then echo "ERROR: backend did not become ready; see $E2E_LOG_DIR/backend.log." >&2 @@ -242,7 +251,7 @@ e2e: frontend-config frontend-e2e-typecheck return 1 fi - {{ direnv_prefix }} bash -c 'cd frontend && exec env -u NO_COLOR trunk serve --address "$E2E_FRONTEND_HOST" --port "$E2E_FRONTEND_PORT" --no-autoreload --skip-version-check --offline' > "$E2E_LOG_DIR/frontend.log" 2>&1 & + {{ direnv_prefix }} bash -c 'cd frontend && exec env -u NO_COLOR trunk serve --address "$E2E_FRONTEND_HOST" --port "$E2E_FRONTEND_PORT" --no-autoreload --skip-version-check --offline --no-sri' > "$E2E_LOG_DIR/frontend.log" 2>&1 & frontend_pid=$! if ! memory_map_wait_for_http "$E2E_FRONTEND_URL/" 120; then echo "ERROR: frontend did not become ready; see $E2E_LOG_DIR/frontend.log." >&2 diff --git a/scripts/service-graph.sh b/scripts/service-graph.sh index 9d9386e..03350dc 100644 --- a/scripts/service-graph.sh +++ b/scripts/service-graph.sh @@ -65,6 +65,27 @@ memory_map_stop_pid() { fi } +# Stop whatever is still listening on a local TCP port. The e2e servers run +# behind a `direnv exec`/bash wrapper, so the tracked pid is the wrapper and the +# real server (backend, trunk) can survive on its port after the wrapper is +# killed, blocking the next local run. This targets only the given port, so it +# never touches unrelated processes. No-op if `ss` is unavailable (e.g. CI, +# which has no wrapper and so does not orphan in the first place). +memory_map_free_port() { + local port="${1:-}" + local pid + + [[ -n "${port}" ]] || return 0 + command -v ss >/dev/null 2>&1 || return 0 + + # Best-effort cleanup; the pipeline's intermediate exit codes are irrelevant. + # shellcheck disable=SC2312 + for pid in $(ss -ltnp 2>/dev/null | grep ":${port} " | + grep -oE 'pid=[0-9]+' | grep -oE '[0-9]+' | sort -u); do + kill "${pid}" 2>/dev/null || true + done +} + memory_map_with_process_compose() { local port="$1" local log_path="$2"