evOo-local/scripts/free-e2e-ports.sh
Claude 2f05ab6a81
ci: give each runner on a shared host its own E2E ports
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
2026-10-02 15:41:55 +00:00

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