From eaab5d175e7e7e727c5b0f4041aaa7ed1c15c2bd Mon Sep 17 00:00:00 2001 From: latenighthackathon Date: Sun, 29 Mar 2026 14:53:24 -0500 Subject: [PATCH 1/3] fix(install): add trap handler to spin() for clean Ctrl+C teardown spin() runs commands in the background with a spinner animation but had no signal handler. Pressing Ctrl+C during installation would kill the parent shell while leaving the background process running and the temp log file on disk. Add INT/TERM trap inside spin() that kills the background process and removes the temp file, then restore default signal handling after the background process exits normally. Closes #1020 --- install.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/install.sh b/install.sh index f81ac7f457b..936ca177aa7 100755 --- a/install.sh +++ b/install.sh @@ -210,11 +210,17 @@ spin() { local pid=$! i=0 local frames=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏') + # Ensure Ctrl+C kills the background process and cleans up the temp file. + trap 'kill "$pid" 2>/dev/null; rm -f "$log"; exit 130' INT TERM + while kill -0 "$pid" 2>/dev/null; do printf "\r ${C_GREEN}%s${C_RESET} %s" "${frames[$((i++ % 10))]}" "$msg" sleep 0.08 done + # Restore default signal handling after the background process exits. + trap - INT TERM + if wait "$pid"; then local status=0 else From 587cb94522f6ccf3322ad83cbdc655fdc572613f Mon Sep 17 00:00:00 2001 From: latenighthackathon Date: Sun, 29 Mar 2026 15:12:57 -0500 Subject: [PATCH 2/3] fix(install): add global EXIT trap for robust cleanup on all exit paths The function-scoped INT/TERM trap in spin() handles Ctrl+C well, but set -e triggering between trap clearance and explicit rm -f could leak temp files or orphan background processes. Add global _cleanup_pids and _cleanup_files arrays with an EXIT trap that catches all exit paths. spin() registers its PID and temp file on entry and deregisters on normal completion. This defense-in-depth ensures cleanup even on unexpected exits. Addresses CodeRabbit review suggestion. --- install.sh | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/install.sh b/install.sh index 936ca177aa7..0aa4ee1a307 100755 --- a/install.sh +++ b/install.sh @@ -6,6 +6,20 @@ set -euo pipefail +# Global cleanup state — ensures background processes are killed and temp files +# are removed on any exit path (set -e, unhandled signal, unexpected error). +_cleanup_pids=() +_cleanup_files=() +_global_cleanup() { + for pid in "${_cleanup_pids[@]:-}"; do + kill "$pid" 2>/dev/null || true + done + for f in "${_cleanup_files[@]:-}"; do + rm -f "$f" 2>/dev/null || true + done +} +trap _global_cleanup EXIT + SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)" DEFAULT_NEMOCLAW_VERSION="0.1.0" TOTAL_STEPS=3 @@ -210,6 +224,10 @@ spin() { local pid=$! i=0 local frames=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏') + # Register with global cleanup so any exit path reaps the child and temp file. + _cleanup_pids+=("$pid") + _cleanup_files+=("$log") + # Ensure Ctrl+C kills the background process and cleans up the temp file. trap 'kill "$pid" 2>/dev/null; rm -f "$log"; exit 130' INT TERM @@ -226,6 +244,11 @@ spin() { else local status=$? fi + + # Remove from global cleanup since we handled it here. + _cleanup_pids=("${_cleanup_pids[@]/$pid/}") + _cleanup_files=("${_cleanup_files[@]/$log/}") + if [[ $status -eq 0 ]]; then printf "\r ${C_GREEN}✓${C_RESET} %s\n" "$msg" else From b7443c9749b06f578df580430ec63eb943082d47 Mon Sep 17 00:00:00 2001 From: latenighthackathon Date: Sun, 29 Mar 2026 17:23:30 -0500 Subject: [PATCH 3/3] fix(install): defer cleanup array deregistration until after rm/kill Move _cleanup_pids and _cleanup_files deregistration to after the actual cleanup actions (rm -f, pid termination) so the global EXIT trap still covers these resources if a signal arrives mid-cleanup. Closes CodeRabbit review comment on #1070. Signed-off-by: latenighthackathon --- install.sh | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/install.sh b/install.sh index 0aa4ee1a307..50a0439ebb0 100755 --- a/install.sh +++ b/install.sh @@ -245,10 +245,6 @@ spin() { local status=$? fi - # Remove from global cleanup since we handled it here. - _cleanup_pids=("${_cleanup_pids[@]/$pid/}") - _cleanup_files=("${_cleanup_files[@]/$log/}") - if [[ $status -eq 0 ]]; then printf "\r ${C_GREEN}✓${C_RESET} %s\n" "$msg" else @@ -257,6 +253,11 @@ spin() { printf "\n" fi rm -f "$log" + + # Deregister only after cleanup actions are complete, so the global EXIT + # trap still covers this pid/log if a signal arrives before this point. + _cleanup_pids=("${_cleanup_pids[@]/$pid/}") + _cleanup_files=("${_cleanup_files[@]/$log/}") return $status }