Manages child processes for multi-repo file watching. Each watched repository gets a ``code-review-graph watch`` child process managed via :mod:`subprocess`. No external tools (tmux, screen, etc.) are required.
| 525 | |
| 526 | |
| 527 | class WatchDaemon: |
| 528 | """Manages child processes for multi-repo file watching. |
| 529 | |
| 530 | Each watched repository gets a ``code-review-graph watch`` child process |
| 531 | managed via :mod:`subprocess`. No external tools (tmux, screen, etc.) |
| 532 | are required. |
| 533 | """ |
| 534 | |
| 535 | def __init__( |
| 536 | self, |
| 537 | config: DaemonConfig | None = None, |
| 538 | config_path: Path | None = None, |
| 539 | ) -> None: |
| 540 | self._config: DaemonConfig = config or load_config(config_path) |
| 541 | self._config_path: Path = config_path or CONFIG_PATH |
| 542 | self._state_path: Path = STATE_PATH |
| 543 | self._children: dict[str, subprocess.Popen[bytes]] = {} |
| 544 | self._current_repos: dict[str, WatchRepo] = {} |
| 545 | self._config_watcher: ConfigWatcher | None = None |
| 546 | self._health_thread: threading.Thread | None = None |
| 547 | self._health_stop: threading.Event = threading.Event() |
| 548 | self._lock: threading.Lock = threading.Lock() |
| 549 | |
| 550 | # ------------------------------------------------------------------ |
| 551 | # Public interface |
| 552 | # ------------------------------------------------------------------ |
| 553 | |
| 554 | def start(self) -> None: |
| 555 | """Spawn a watcher child process for each configured repo.""" |
| 556 | logger.info("Starting daemon '%s'", self._config.session_name) |
| 557 | |
| 558 | # Auto-register repos in the central registry |
| 559 | from .registry import Registry |
| 560 | |
| 561 | registry = Registry() |
| 562 | for repo in self._config.repos: |
| 563 | registry.register(repo.path, alias=repo.alias) |
| 564 | |
| 565 | # Build initial graph for repos that lack a database |
| 566 | for repo in self._config.repos: |
| 567 | db_path = Path(repo.path) / ".code-review-graph" / "graph.db" |
| 568 | if not db_path.exists(): |
| 569 | self._initial_build(repo) |
| 570 | |
| 571 | # Spawn a watcher child for every repo |
| 572 | for repo in self._config.repos: |
| 573 | self._start_watcher(repo) |
| 574 | |
| 575 | # Track current state |
| 576 | self._current_repos = {r.alias: r for r in self._config.repos} |
| 577 | |
| 578 | # Persist child PIDs to disk for cross-process status queries |
| 579 | self._save_state() |
| 580 | |
| 581 | # Start watching the config file for live changes |
| 582 | self.start_config_watcher() |
| 583 | |
| 584 | # Start health checker to auto-restart dead watchers |
no outgoing calls