Stop the dev server (SIGTERM then SIGKILL if needed). Uses psutil to terminate the entire process tree, ensuring child processes (like Node.js) are also terminated. Returns: Tuple of (success, message)
(self)
| 377 | return False, f"Failed to start dev server: {e}" |
| 378 | |
| 379 | async def stop(self) -> tuple[bool, str]: |
| 380 | """ |
| 381 | Stop the dev server (SIGTERM then SIGKILL if needed). |
| 382 | |
| 383 | Uses psutil to terminate the entire process tree, ensuring |
| 384 | child processes (like Node.js) are also terminated. |
| 385 | |
| 386 | Returns: |
| 387 | Tuple of (success, message) |
| 388 | """ |
| 389 | if not self.process or self.status == "stopped": |
| 390 | return False, "Dev server is not running" |
| 391 | |
| 392 | try: |
| 393 | # Cancel output streaming |
| 394 | if self._output_task: |
| 395 | self._output_task.cancel() |
| 396 | try: |
| 397 | await self._output_task |
| 398 | except asyncio.CancelledError: |
| 399 | pass |
| 400 | |
| 401 | # Use shared utility to terminate the entire process tree |
| 402 | # This is important for dev servers that spawn child processes (like Node.js) |
| 403 | proc = self.process # Capture reference before async call |
| 404 | loop = asyncio.get_running_loop() |
| 405 | result = await loop.run_in_executor(None, kill_process_tree, proc, 5.0) |
| 406 | logger.debug( |
| 407 | "Process tree kill result: status=%s, children=%d (terminated=%d, killed=%d)", |
| 408 | result.status, result.children_found, |
| 409 | result.children_terminated, result.children_killed |
| 410 | ) |
| 411 | |
| 412 | self._remove_lock() |
| 413 | self.status = "stopped" |
| 414 | self.process = None |
| 415 | self.started_at = None |
| 416 | self._detected_url = None |
| 417 | self._command = None |
| 418 | |
| 419 | return True, "Dev server stopped" |
| 420 | except Exception as e: |
| 421 | logger.exception("Failed to stop dev server") |
| 422 | return False, f"Failed to stop dev server: {e}" |
| 423 | |
| 424 | async def healthcheck(self) -> bool: |
| 425 | """ |
no test coverage detected