From 8c8d5bca113220bdc6070143d1229593ec365a79 Mon Sep 17 00:00:00 2001 From: Justin Visser Date: Wed, 10 Jun 2026 14:27:49 +0200 Subject: [PATCH] dev: dev.sh detects stale servers and offers to kill them Before starting, each port (8080, 5173) is checked via ss; an existing listener is reported with PID and command, and an interactive run asks permission to kill it (default yes). Non-interactive runs abort instead: never kill processes silently. Born from a real incident: an orphaned smoke-test server held 8080 and dev.sh only said 'address already in use'. --- dev.sh | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/dev.sh b/dev.sh index 131c26f..700e926 100755 --- a/dev.sh +++ b/dev.sh @@ -3,10 +3,44 @@ # - Go API on localhost:8080 # - Vite dev server on localhost:5173 (proxies /api to the Go server) # One Ctrl-C stops everything; if either server dies, the other is stopped. +# If a port is already taken (usually a stale server), offers to kill it. set -euo pipefail cd "$(dirname "$0")" +# free_port checks whether something is listening on the port and, when run +# interactively, offers to kill it. Otherwise it refuses to continue: never +# kill processes silently. +free_port() { + local port="$1" name="$2" + local listener_pid + listener_pid=$(ss -tlnp "sport = :$port" 2>/dev/null | grep -oP 'pid=\K[0-9]+' | head -1 || true) + [ -z "$listener_pid" ] && return 0 + + local listener_cmd + listener_cmd=$(ps -p "$listener_pid" -o comm= 2>/dev/null || echo unknown) + echo "dev.sh: port $port ($name) is already in use by PID $listener_pid ($listener_cmd)." + + local answer="n" + if [ -t 0 ]; then + read -r -p "Kill it and continue? [Y/n] " answer + answer=${answer:-y} + fi + case "$answer" in + [Yy]*) + kill "$listener_pid" + sleep 0.5 + ;; + *) + echo "dev.sh: aborting; free port $port first." + exit 1 + ;; + esac +} + +free_port 8080 "Go API" +free_port 5173 "Vite" + if [ ! -d web/node_modules ]; then echo "dev.sh: first run, installing frontend dependencies..." (cd web && npm install)