feat: handle cli processes cleanup when terminal window is closed

This commit is contained in:
Daulet Amirkhanov 2025-10-09 19:47:52 +01:00
parent 079a5528b0
commit 2c8d8e8058

View file

@ -180,33 +180,42 @@ def main() -> int:
def signal_handler(signum, frame): def signal_handler(signum, frame):
"""Handle Ctrl+C and other termination signals""" """Handle Ctrl+C and other termination signals"""
nonlocal spawned_pids, docker_container nonlocal spawned_pids, docker_container
fmt.echo("\nShutting down UI server...")
try:
fmt.echo("\nShutting down UI server...")
except (BrokenPipeError, OSError):
pass
# First, stop Docker container if running # First, stop Docker container if running
if docker_container: if docker_container:
try: try:
fmt.echo(f"Stopping Docker container {docker_container}...")
result = subprocess.run( result = subprocess.run(
["docker", "stop", docker_container], ["docker", "stop", docker_container],
capture_output=True, capture_output=True,
timeout=10, timeout=10,
check=False, check=False,
) )
if result.returncode == 0: try:
fmt.success(f"✓ Docker container {docker_container} stopped.") if result.returncode == 0:
else: fmt.success(f"✓ Docker container {docker_container} stopped.")
fmt.warning( else:
f"Could not stop container {docker_container}: {result.stderr.decode()}" fmt.warning(
) f"Could not stop container {docker_container}: {result.stderr.decode()}"
)
except (BrokenPipeError, OSError):
pass
except subprocess.TimeoutExpired: except subprocess.TimeoutExpired:
fmt.warning( try:
f"Timeout stopping container {docker_container}, forcing removal..." fmt.warning(
) f"Timeout stopping container {docker_container}, forcing removal..."
)
except (BrokenPipeError, OSError):
pass
subprocess.run( subprocess.run(
["docker", "rm", "-f", docker_container], capture_output=True, check=False ["docker", "rm", "-f", docker_container], capture_output=True, check=False
) )
except Exception as e: except Exception:
fmt.warning(f"Could not stop Docker container {docker_container}: {e}") pass
# Then, stop regular processes # Then, stop regular processes
for pid in spawned_pids: for pid in spawned_pids:
@ -215,7 +224,10 @@ def main() -> int:
# Unix-like systems: Use process groups # Unix-like systems: Use process groups
pgid = os.getpgid(pid) pgid = os.getpgid(pid)
os.killpg(pgid, signal.SIGTERM) os.killpg(pgid, signal.SIGTERM)
fmt.success(f"✓ Process group {pgid} (PID {pid}) terminated.") try:
fmt.success(f"✓ Process group {pgid} (PID {pid}) terminated.")
except (BrokenPipeError, OSError):
pass
else: else:
# Windows: Use taskkill to terminate process and its children # Windows: Use taskkill to terminate process and its children
subprocess.run( subprocess.run(
@ -223,14 +235,19 @@ def main() -> int:
capture_output=True, capture_output=True,
check=False, check=False,
) )
fmt.success(f"✓ Process {pid} and its children terminated.") try:
except (OSError, ProcessLookupError, subprocess.SubprocessError) as e: fmt.success(f"✓ Process {pid} and its children terminated.")
fmt.warning(f"Could not terminate process {pid}: {e}") except (BrokenPipeError, OSError):
pass
except (OSError, ProcessLookupError, subprocess.SubprocessError):
pass
sys.exit(0) sys.exit(0)
signal.signal(signal.SIGINT, signal_handler) # Ctrl+C signal.signal(signal.SIGINT, signal_handler) # Ctrl+C
signal.signal(signal.SIGTERM, signal_handler) # Termination request signal.signal(signal.SIGTERM, signal_handler) # Termination request
if hasattr(signal, "SIGHUP"):
signal.signal(signal.SIGHUP, signal_handler)
try: try:
from cognee import start_ui from cognee import start_ui