feat(democracy): find the running stacks' worktrees live for the resource cleanup

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kevin Veen-BirkenbachandClaude Opus 5.5 committed 2026-10-30 17:41:10 +01:00
1 parent d6fb75ba22
commit 239e15b970
2 files changed
+30 -7

No files matched your search

+16 -6
View File
@@ -23,14 +23,15 @@ Environment:
Config keys, all required: disk, thresholds_gb {warn, stop, critical}, Config keys, all required: disk, thresholds_gb {warn, stop, critical},
projection_minutes, interval_seconds, cache_interval_seconds, heavy_commands, projection_minutes, interval_seconds, cache_interval_seconds, heavy_commands,
never_block (regexes), cleanup (shell commands), worktree_cleanup (shell never_block (regexes), cleanup (shell commands), worktree_cleanup (shell
commands with {worktree}), worktrees (globs), cache_dirs, cache_budget_percent, commands with {worktree}), worktrees_command (shell command printing the
running stacks' worktree paths, one per line), worktree_marker (path relative
to a worktree that must exist), cache_dirs, cache_budget_percent,
slot_need_gb {kind: GB}, notify (argv the alert text is appended to). slot_need_gb {kind: GB}, notify (argv the alert text is appended to).
""" """
from __future__ import annotations from __future__ import annotations
import argparse import argparse
import glob
import json import json
import os import os
import re import re
@@ -263,12 +264,21 @@ def trim_caches(cfg: dict) -> float:
return freed / GB return freed / GB
def running_worktrees(cfg: dict) -> list[str]:
try:
out = subprocess.run(
cfg["worktrees_command"], shell=True, capture_output=True, text=True, timeout=60, check=False
).stdout
except subprocess.TimeoutExpired:
return []
trees = sorted({line.strip() for line in out.splitlines() if line.strip()})
return [t for t in trees if os.path.exists(os.path.join(t, cfg["worktree_marker"]))]
def run_cleanup(cfg: dict) -> list[str]: def run_cleanup(cfg: dict) -> list[str]:
commands = list(cfg["cleanup"]) commands = list(cfg["cleanup"])
for pattern in cfg["worktrees"]: for tree in running_worktrees(cfg):
for tree in sorted(glob.glob(os.path.expanduser(pattern))): commands += [t.format(worktree=shlex.quote(tree)) for t in cfg["worktree_cleanup"]]
if os.path.isdir(tree):
commands += [t.format(worktree=shlex.quote(tree)) for t in cfg["worktree_cleanup"]]
results = [] results = []
for command in commands: for command in commands:
try: try:
+14 -1
View File
@@ -59,7 +59,8 @@ class TestWatchCommands(unittest.TestCase):
"never_block": ["^make swarm-down"], "never_block": ["^make swarm-down"],
"cleanup": [], "cleanup": [],
"worktree_cleanup": [], "worktree_cleanup": [],
"worktrees": [], "worktrees_command": "true",
"worktree_marker": "Makefile",
"cache_dirs": [str(self.cache)], "cache_dirs": [str(self.cache)],
"cache_budget_percent": 3, "cache_budget_percent": 3,
"slot_need_gb": {"swarm": 30}, "slot_need_gb": {"swarm": 30},
@@ -128,6 +129,18 @@ class TestWatchCommands(unittest.TestCase):
self.assertFalse(old.exists()) self.assertFalse(old.exists())
self.assertTrue(new.exists()) self.assertTrue(new.exists())
def test_full_cleanup_reaches_only_marked_running_worktrees(self):
marked, foreign = self.base / "marked", self.base / "foreign"
for tree in (marked, foreign):
tree.mkdir()
(marked / "Makefile").write_text("", encoding="utf-8")
self.cfg["worktrees_command"] = f"echo {marked}; echo {foreign}; echo {marked}"
self.cfg["worktree_cleanup"] = ["echo x >> {worktree}/cleaned"]
self._write_config()
self.assertEqual(self._run("cleanup", "--full").returncode, 0)
self.assertEqual((marked / "cleaned").read_text(encoding="utf-8"), "x\n")
self.assertFalse((foreign / "cleaned").exists())
def test_without_config_every_command_is_a_no_op(self): def test_without_config_every_command_is_a_no_op(self):
self.config.unlink() self.config.unlink()
result = self._run("status") result = self._run("status")