mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
fix(dev): reap orphaned agent processes on dev/staging exit
Ctrl+C on `just dev`/`just staging` tears down the Tauri app before its in-process system sweep can finish, so agent workers spawned in their own process groups survive as orphans and accumulate across sessions. Add an EXIT trap that reaps this instance's agents via the PID-file receipts the desktop already writes (one PGID per agent under `<app-data>/agents/agent-pids/`). The app-data dir is keyed by the bundle identifier, so the cleanup is scoped exactly to the instance that ran the recipe — the main checkout never reaps a worktree's agents, or vice versa. PID files are matched instead of the `SPROUT_MANAGED_AGENT` env marker because macOS `pkill -f` matches only argv, not the environment, so an env-marker match would silently reap nothing. Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
This commit is contained in:
parent
e5f0c32648
commit
c8270dc429
@@ -277,6 +277,10 @@ dev *ARGS: _ensure-sidecar-stubs
|
||||
cd {{desktop_dir}}
|
||||
[[ -d node_modules ]] || pnpm install
|
||||
source ../scripts/instance-env.sh
|
||||
# Ctrl+C kills the Tauri app before its in-process sweep finishes, leaking
|
||||
# agent workers. Reap this instance's agents on exit as a backstop.
|
||||
INSTANCE_ID=$(node -e "console.log(JSON.parse(process.env.SPROUT_TAURI_CONFIG).identifier)")
|
||||
trap '../scripts/cleanup-instance-agents.sh "$INSTANCE_ID"' EXIT
|
||||
echo "Starting on Vite port ${SPROUT_VITE_PORT}, relay ${SPROUT_RELAY_URL}"
|
||||
pnpm exec tauri dev --features mesh-llm --config "$SPROUT_TAURI_CONFIG" {{ARGS}}
|
||||
|
||||
@@ -294,6 +298,10 @@ staging *ARGS: _ensure-sidecar-stubs
|
||||
cd {{desktop_dir}}
|
||||
source ../scripts/instance-env.sh
|
||||
export SPROUT_RELAY_URL="wss://sprout-oss.stage.blox.sqprod.co"
|
||||
# Ctrl+C kills the Tauri app before its in-process sweep finishes, leaking
|
||||
# agent workers. Reap this instance's agents on exit as a backstop.
|
||||
INSTANCE_ID=$(node -e "console.log(JSON.parse(process.env.SPROUT_TAURI_CONFIG).identifier)")
|
||||
trap '../scripts/cleanup-instance-agents.sh "$INSTANCE_ID"' EXIT
|
||||
echo "Starting staging on Vite port ${SPROUT_VITE_PORT}, relay ${SPROUT_RELAY_URL}"
|
||||
pnpm exec tauri dev --features mesh-llm --config "$SPROUT_TAURI_CONFIG" {{ARGS}}
|
||||
|
||||
|
||||
Executable
+59
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env bash
|
||||
# Reap the agent processes belonging to a single desktop instance.
|
||||
#
|
||||
# `tauri dev` Ctrl+C tears down the Rust app before its in-process system sweep
|
||||
# can finish, so agent workers (goose, sprout-agent, ...) it spawned in their
|
||||
# own process groups survive as orphans. This script is the shell-side backstop:
|
||||
# run it from an EXIT trap in the `just dev`/`just staging` recipes.
|
||||
#
|
||||
# It reads the PID-file receipts the desktop already writes — one file per agent
|
||||
# under `<app-data>/agents/agent-pids/<pubkey>.pid`, each containing the agent's
|
||||
# PGID (agents are spawned with `process_group(0)`, so PID == PGID). Killing by
|
||||
# PGID reaches the whole agent subtree. We deliberately do NOT match the
|
||||
# `SPROUT_MANAGED_AGENT` env var from the shell: on macOS `pkill -f` matches only
|
||||
# argv, not the environment, so an env-marker match silently reaps nothing.
|
||||
#
|
||||
# Scoping is exact because the app-data directory is keyed by the instance's
|
||||
# bundle identifier, so this only ever touches the receipts this instance wrote
|
||||
# (the main checkout never reaps a worktree's agents, or vice versa).
|
||||
#
|
||||
# Usage: cleanup-instance-agents.sh <instance-id>
|
||||
# <instance-id> is the desktop bundle identifier, e.g. `xyz.block.sprout.app.dev`
|
||||
# (main checkout) or `xyz.block.sprout.app.dev.my-branch` (a worktree).
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
instance_id="${1:-}"
|
||||
if [[ -z "$instance_id" ]]; then
|
||||
echo "cleanup-instance-agents: no instance id given, skipping" >&2
|
||||
exit 0
|
||||
fi
|
||||
|
||||
case "$(uname -s)" in
|
||||
Darwin) app_data="$HOME/Library/Application Support/$instance_id" ;;
|
||||
*) app_data="${XDG_DATA_HOME:-$HOME/.local/share}/$instance_id" ;;
|
||||
esac
|
||||
|
||||
pids_dir="$app_data/agents/agent-pids"
|
||||
[[ -d "$pids_dir" ]] || exit 0
|
||||
|
||||
shopt -s nullglob
|
||||
pgids=()
|
||||
for pid_file in "$pids_dir"/*.pid; do
|
||||
pgid="$(<"$pid_file")"
|
||||
pgid="${pgid//[$'\t\r\n ']/}"
|
||||
[[ "$pgid" =~ ^[0-9]+$ ]] || continue
|
||||
pgids+=("$pgid")
|
||||
done
|
||||
[[ ${#pgids[@]} -gt 0 ]] || exit 0
|
||||
|
||||
# SIGTERM the whole group first, give it a moment, then SIGKILL survivors.
|
||||
# `kill -- -<pgid>` targets the process group. Failures are expected and fine
|
||||
# (already-dead group, recycled PGID owned by someone else we can't signal).
|
||||
for pgid in "${pgids[@]}"; do
|
||||
kill -TERM -- "-$pgid" 2>/dev/null || true
|
||||
done
|
||||
sleep 0.2
|
||||
for pgid in "${pgids[@]}"; do
|
||||
kill -KILL -- "-$pgid" 2>/dev/null || true
|
||||
done
|
||||
Reference in New Issue
Block a user