Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions frontend/Trunk.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
13 changes: 11 additions & 2 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -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=""
Expand All @@ -230,19 +234,24 @@ 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
tail -n 80 "$E2E_LOG_DIR/backend.log" >&2 || true
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
Expand Down
21 changes: 21 additions & 0 deletions scripts/service-graph.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down