Skip to content

Commit b93417b

Browse files
committed
fix(audit): add category grouping to update mode
Group tools by category during make update with headers: - 🐍 Python Development (11 tools) - 📦 Node.js Development (6 tools) - etc. Tools within each category still process in parallel.
1 parent 5a7a112 commit b93417b

2 files changed

Lines changed: 594 additions & 578 deletions

File tree

audit.py

Lines changed: 97 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -363,91 +363,107 @@ def cmd_update(args: argparse.Namespace) -> int:
363363
print(f"# Estimated time: ~{est_time}s (timeout=3s per tool, {MAX_WORKERS} workers)", file=sys.stderr)
364364
print("", file=sys.stderr)
365365

366-
# Parallel audit with progress tracking
366+
# Group tools by category for organized output
367+
from cli_audit.render import CATEGORY_ORDER, CATEGORY_ICON, CATEGORY_DESC
368+
categorized: dict[str, list] = {}
369+
for tool in tools_list:
370+
cat = tool.category or "general"
371+
if cat not in categorized:
372+
categorized[cat] = []
373+
categorized[cat].append(tool)
374+
sorted_cats = sorted(categorized.keys(), key=lambda c: CATEGORY_ORDER.get(c, 99))
375+
376+
# Parallel audit with progress tracking, grouped by category
367377
results = []
368378
completed = 0
369379

370380
try:
371-
with ThreadPoolExecutor(max_workers=min(MAX_WORKERS, total)) as executor:
372-
future_to_tool = {executor.submit(audit_tool, tool, None): tool for tool in tools_list}
373-
374-
for future in as_completed(future_to_tool):
375-
tool = future_to_tool[future]
376-
try:
377-
result = future.result()
378-
results.append(result)
379-
completed += 1
380-
381-
# Progress
382-
inst = result.get("installed", "")
383-
latest = result.get("latest_upstream", "")
384-
status = result.get("status", "")
385-
386-
# ANSI colors for all platforms
387-
GREEN = "\033[32m"
388-
BOLD_GREEN = "\033[1;32m"
389-
YELLOW = "\033[33m"
390-
BLUE = "\033[34m"
391-
RESET = "\033[0m"
392-
393-
# Color the installed version based on status
394-
if status == "UP-TO-DATE":
395-
inst_color = GREEN
396-
latest_color = GREEN
397-
op = "==="
398-
elif status == "OUTDATED":
399-
inst_color = YELLOW
400-
latest_color = BOLD_GREEN # Latest is newer, make it bold green
401-
op = "!=="
402-
elif status == "CONFLICT":
403-
inst_color = YELLOW
404-
latest_color = BOLD_GREEN
405-
op = "⚠️"
406-
else: # NOT INSTALLED, UNKNOWN
407-
inst_color = BLUE # Blue for not-installed
408-
latest_color = BLUE
409-
op = "?"
410-
411-
inst_display = inst if inst else "n/a"
412-
latest_display = latest if latest else "n/a"
413-
414-
# Add pinned/skip markers
415-
from cli_audit.catalog import ToolCatalog
416-
catalog = ToolCatalog()
417-
markers = []
418-
if catalog.is_pinned(tool.name):
419-
markers.append("PINNED")
420-
if catalog.should_skip(tool.name, latest):
421-
markers.append("SKIP")
422-
423-
marker_str = f" [{' '.join(markers)}]" if markers else ""
424-
inst_fmt = f"{inst_color}{inst_display}{RESET}"
425-
latest_fmt = f"{latest_color}{latest_display}{RESET}"
426-
msg = f"# [{completed}/{total}] {tool.name} (installed: {inst_fmt} {op} latest: {latest_fmt}){marker_str}"
427-
428-
print(msg, file=sys.stderr, flush=True)
429-
430-
except Exception as e:
431-
completed += 1
432-
print(f"# [{completed}/{total}] {tool.name} (failed: {e})", file=sys.stderr, flush=True)
433-
434-
# Add failure entry
435-
results.append({
436-
"tool": tool.name,
437-
"category": tool.category,
438-
"installed": "",
439-
"installed_method": "",
440-
"installed_version": "",
441-
"installed_path_selected": "",
442-
"classification_reason_selected": "Detection failed",
443-
"latest_upstream": "",
444-
"latest_version": "",
445-
"upstream_method": tool.source_kind,
446-
"status": "UNKNOWN",
447-
"tool_url": tool_homepage_url(tool),
448-
"latest_url": "",
449-
"hint": "",
450-
})
381+
for category in sorted_cats:
382+
cat_tools = categorized[category]
383+
icon = CATEGORY_ICON.get(category, "📦")
384+
desc = CATEGORY_DESC.get(category, category)
385+
print(f"\n# {icon} {desc} ({len(cat_tools)} tools)", file=sys.stderr)
386+
387+
with ThreadPoolExecutor(max_workers=min(MAX_WORKERS, len(cat_tools))) as executor:
388+
future_to_tool = {executor.submit(audit_tool, tool, None): tool for tool in cat_tools}
389+
390+
for future in as_completed(future_to_tool):
391+
tool = future_to_tool[future]
392+
try:
393+
result = future.result()
394+
results.append(result)
395+
completed += 1
396+
397+
# Progress
398+
inst = result.get("installed", "")
399+
latest = result.get("latest_upstream", "")
400+
status = result.get("status", "")
401+
402+
# ANSI colors for all platforms
403+
GREEN = "\033[32m"
404+
BOLD_GREEN = "\033[1;32m"
405+
YELLOW = "\033[33m"
406+
BLUE = "\033[34m"
407+
RESET = "\033[0m"
408+
409+
# Color the installed version based on status
410+
if status == "UP-TO-DATE":
411+
inst_color = GREEN
412+
latest_color = GREEN
413+
op = "==="
414+
elif status == "OUTDATED":
415+
inst_color = YELLOW
416+
latest_color = BOLD_GREEN # Latest is newer, make it bold green
417+
op = "!=="
418+
elif status == "CONFLICT":
419+
inst_color = YELLOW
420+
latest_color = BOLD_GREEN
421+
op = "⚠️"
422+
else: # NOT INSTALLED, UNKNOWN
423+
inst_color = BLUE # Blue for not-installed
424+
latest_color = BLUE
425+
op = "?"
426+
427+
inst_display = inst if inst else "n/a"
428+
latest_display = latest if latest else "n/a"
429+
430+
# Add pinned/skip markers
431+
from cli_audit.catalog import ToolCatalog
432+
catalog = ToolCatalog()
433+
markers = []
434+
if catalog.is_pinned(tool.name):
435+
markers.append("PINNED")
436+
if catalog.should_skip(tool.name, latest):
437+
markers.append("SKIP")
438+
439+
marker_str = f" [{' '.join(markers)}]" if markers else ""
440+
inst_fmt = f"{inst_color}{inst_display}{RESET}"
441+
latest_fmt = f"{latest_color}{latest_display}{RESET}"
442+
msg = f"# [{completed}/{total}] {tool.name} (installed: {inst_fmt} {op} latest: {latest_fmt}){marker_str}"
443+
444+
print(msg, file=sys.stderr, flush=True)
445+
446+
except Exception as e:
447+
completed += 1
448+
print(f"# [{completed}/{total}] {tool.name} (failed: {e})", file=sys.stderr, flush=True)
449+
450+
# Add failure entry
451+
results.append({
452+
"tool": tool.name,
453+
"category": tool.category,
454+
"installed": "",
455+
"installed_method": "",
456+
"installed_version": "",
457+
"installed_path_selected": "",
458+
"classification_reason_selected": "Detection failed",
459+
"latest_upstream": "",
460+
"latest_version": "",
461+
"upstream_method": tool.source_kind,
462+
"status": "UNKNOWN",
463+
"tool_url": tool_homepage_url(tool),
464+
"latest_url": "",
465+
"hint": "",
466+
})
451467
except KeyboardInterrupt:
452468
# Shutdown executor immediately without waiting for threads
453469
executor.shutdown(wait=False, cancel_futures=True)

0 commit comments

Comments
 (0)