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
37 changes: 29 additions & 8 deletions bin/conductor-archive
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,35 @@ PORT=${WORKSPACE_PORT:-${PORT:-3000}}

echo "Archiving Conductor workspace (port ${PORT})..."

# Kill any processes listening on this workspace's ports
for p in "$PORT" "$((PORT + 36))"; do
pid=$(lsof -ti :"$p" 2>/dev/null || true)
if [ -n "$pid" ]; then
echo "Stopping process on port $p (pid $pid)..."
kill "$pid" 2>/dev/null || true
fi
done
# Reap every process listening on this workspace's ports (Rails on PORT, Vite on
# PORT+36). lsof can return several PIDs for one port β€” puma cluster master +
# workers share the listening socket, and `bin/vite dev` spawns esbuild children
# β€” so the old quoted `kill "$pid"` passed the whole newline-joined list as a
# single argument and silently failed, orphaning the extra processes. They stayed
# bound to the port and collided with the next workspace to recycle the block.
# Word-split the list, SIGTERM each, then SIGKILL any survivors after a grace beat.
free_ports() {
killed=""
for p in "$@"; do
pids=$(lsof -ti :"$p" 2>/dev/null || true)
[ -n "$pids" ] || continue
echo "Stopping process(es) on port $p (pid $(echo $pids | tr '\n' ' '))..."
# shellcheck disable=SC2086 -- intentional word-split of the multi-PID list
kill $pids 2>/dev/null || true
killed=1
done
[ -n "$killed" ] || return 0
sleep 1
for p in "$@"; do
pids=$(lsof -ti :"$p" 2>/dev/null || true)
[ -n "$pids" ] || continue
echo "Force-killing process(es) still on port $p..."
# shellcheck disable=SC2086 -- intentional word-split of the multi-PID list
kill -9 $pids 2>/dev/null || true
done
}

free_ports "$PORT" "$((PORT + 36))"

# Drop workspace-specific databases. Keep `|| true` so a drop failure never
# aborts archiving, but let stderr through so failures are visible in the log
Expand Down
29 changes: 29 additions & 0 deletions bin/conductor-server
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,35 @@ export WORKSPACE_PORT=${WORKSPACE_PORT:-${CONDUCTOR_PORT:-}}
export PORT=${WORKSPACE_PORT:-${PORT:-3000}}
export VITE_RUBY_PORT=$((PORT + 36))

# Reap any ghost servers still bound to this workspace's ports before booting, so
# a leaked process from a prior crashed/force-quit session doesn't cause a bind
# collision. lsof can return several PIDs for one port (puma cluster master +
# workers, or `bin/vite dev` + its esbuild children); word-split the list, SIGTERM
# each, then SIGKILL any survivors after a grace beat. A quoted `kill "$pid"`
# would pass the whole newline-joined list as a single argument and silently fail.
free_ports() {
killed=""
for p in "$@"; do
pids=$(lsof -ti :"$p" 2>/dev/null || true)
[ -n "$pids" ] || continue
echo "Freeing port $p (pid $(echo $pids | tr '\n' ' '))..."
# shellcheck disable=SC2086 -- intentional word-split of the multi-PID list
kill $pids 2>/dev/null || true
killed=1
done
[ -n "$killed" ] || return 0
sleep 1
for p in "$@"; do
pids=$(lsof -ti :"$p" 2>/dev/null || true)
[ -n "$pids" ] || continue
echo "Force-killing process(es) still on port $p..."
# shellcheck disable=SC2086 -- intentional word-split of the multi-PID list
kill -9 $pids 2>/dev/null || true
done
}

free_ports "$PORT" "$VITE_RUBY_PORT"

echo "Starting on http://awbw.local:${PORT} (Vite on :${VITE_RUBY_PORT})"

# Start Vite dev server in the background
Expand Down