diff --git a/.vscode/run-once.sh b/.vscode/run-once.sh new file mode 100755 index 00000000..8ce94520 --- /dev/null +++ b/.vscode/run-once.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +# Ensures only one instance of a command runs at a time. If a previous +# instance is still running (e.g. orphaned after a VS Code crash), it is +# killed before starting the new one. +# +# Kills the entire process tree (not just the stored PID) because `yarn` +# spawns child processes (e.g. rollup/node) that hold the port. After +# killing, waits for the port to be released before starting the new +# instance. +# +# Usage: run-once.sh + +set -euo pipefail + +PIDFILE="$1"; shift +PORT="$1"; shift + +kill_tree() { + local pid="$1" + # Kill children first (depth-first), then the parent. + pkill -TERM -P "$pid" 2>/dev/null || true + kill "$pid" 2>/dev/null || true + tail --pid="$pid" -f /dev/null 2>/dev/null || true +} + +wait_for_port() { + local port="$1" + local attempts=0 + while ss -tlnp | grep -q ":${port} " && [ $attempts -lt 30 ]; do + sleep 0.2 + attempts=$((attempts + 1)) + done +} + +if [ -f "$PIDFILE" ]; then + OLD_PID=$(cat "$PIDFILE" 2>/dev/null || true) + if [ -n "$OLD_PID" ] && kill -0 "$OLD_PID" 2>/dev/null; then + kill_tree "$OLD_PID" + fi + rm -f "$PIDFILE" + wait_for_port "$PORT" +fi + +trap 'rm -f "$PIDFILE"' EXIT +echo $$ > "$PIDFILE" +exec "$@" diff --git a/.vscode/tasks.json b/.vscode/tasks.json index e8cbbcd0..0d1f8767 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -3,8 +3,9 @@ "tasks": [ { "label": "Rollup", - "type": "npm", - "script": "start", + "type": "shell", + "command": "${workspaceFolder}/.vscode/run-once.sh", + "args": ["/tmp/advanced-camera-card-rollup.pid", "10001", "yarn", "start"], "problemMatcher": { "pattern": [ { @@ -22,7 +23,8 @@ }, "isBackground": true, "runOptions": { - "runOn": "folderOpen" + "runOn": "folderOpen", + "instanceLimit": 1 } } ]