Two Linux runners live on the same host. Every Web E2E job first frees "stale" listeners on the fixed ports 3110, 3111, 5180 and 4321 with fuser -k, so a job starting on the second runner killed the Vite server of the job already running on the first one. The victim died with SIGKILL (exit 137), and its remaining tests failed on reloads that could no longer reach a server. This is what made the Web E2E run on this PR red, and it explains the recurring reload failures on main whenever two runs overlapped. The workflow now derives API_PORT, WEB_PORT and WWW_PORT from the runner name (suffix -N means an offset of N times 100), and the port-freeing script follows those settings instead of a hard-coded list. The harness, Vite, the API and the marketing site already read these variables. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
59 lines
1.8 KiB
Bash
Executable File
59 lines
1.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Free stale listeners on dedicated Playwright/E2E ports before harness bind.
|
|
# Never touches live dev stack ports 3100 (API) or 5173 (web).
|
|
# The ports follow API_PORT, WEB_PORT and WWW_PORT so that runners sharing one host,
|
|
# each with its own port set, never kill another job's servers.
|
|
|
|
set -euo pipefail
|
|
|
|
for setting in API_PORT WEB_PORT WWW_PORT; do
|
|
value="${!setting:-}"
|
|
if [[ -n "$value" && ! "$value" =~ ^[0-9]+$ ]]; then
|
|
echo "free-e2e-ports: $setting must be a port number, got: $value" >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
readonly LIVE_PORTS=(3100 5173)
|
|
api_port="${API_PORT:-3110}"
|
|
readonly E2E_PORTS=("$api_port" "$((api_port + 1))" "${WEB_PORT:-5180}" "${WWW_PORT:-4321}")
|
|
|
|
if [[ "$(uname -s)" != "Linux" ]]; then
|
|
echo "free-e2e-ports: skip (not Linux)"
|
|
exit 0
|
|
fi
|
|
|
|
if ! command -v fuser >/dev/null 2>&1; then
|
|
echo "free-e2e-ports: warning: fuser not found; cannot pre-free E2E ports" >&2
|
|
exit 0
|
|
fi
|
|
|
|
for port in "${LIVE_PORTS[@]}"; do
|
|
for e2e_port in "${E2E_PORTS[@]}"; do
|
|
if [[ "$port" -eq "$e2e_port" ]]; then
|
|
echo "free-e2e-ports: internal error: live port $port must not be in E2E_PORTS" >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
done
|
|
|
|
echo "free-e2e-ports: clearing stale listeners on E2E ports (${E2E_PORTS[*]}); live ports ${LIVE_PORTS[*]} untouched"
|
|
|
|
for port in "${E2E_PORTS[@]}"; do
|
|
spec="${port}/tcp"
|
|
if fuser "$spec" >/dev/null 2>&1; then
|
|
echo "free-e2e-ports: port $port in use:"
|
|
fuser -v "$spec" 2>&1 || true
|
|
echo "free-e2e-ports: killing processes on port $port (fuser -k)"
|
|
fuser -k "$spec" 2>&1 || true
|
|
sleep 0.25
|
|
if fuser "$spec" >/dev/null 2>&1; then
|
|
echo "free-e2e-ports: warning: port $port still in use after fuser -k" >&2
|
|
else
|
|
echo "free-e2e-ports: port $port is now free"
|
|
fi
|
|
else
|
|
echo "free-e2e-ports: port $port already free"
|
|
fi
|
|
done
|