Add analyzers, reporters, and utilities
Some checks failed
CI / test (push) Has been cancelled

This commit is contained in:
2026-02-04 14:57:53 +00:00
parent e6fb9a3f46
commit 6b9e2e48ce

View File

@@ -0,0 +1,129 @@
"""Terminal reporter with Rich formatting."""
from typing import Optional
from rich import box
from rich.console import Console
from rich.panel import Panel
from rich.table import Table
from rich.text import Text
from depcheck.config import Config
from depcheck.models import ScanResult, Severity
class TerminalReporter:
"""Report results in terminal-friendly format using Rich."""
def __init__(self, config: Optional[Config] = None):
self.config = config or Config()
self.console = Console()
def report(self, result: ScanResult) -> None:
"""Display the scan result."""
self._print_summary(result)
self._print_vulnerabilities(result)
self._print_outdated_dependencies(result)
def _print_summary(self, result: ScanResult) -> None:
"""Print scan summary."""
status = "✓ All Fresh" if result.outdated_count == 0 and result.vulnerable_count == 0 else "⚠ Issues Found"
status_color = "green" if result.outdated_count == 0 and result.vulnerable_count == 0 else "yellow"
summary = Table(box=box.SIMPLE, show_header=False, padding=0)
summary.add_column()
summary.add_column()
summary.add_row("Status", Text(status, style=status_color))
summary.add_row("Dependencies Scanned", str(len(result.dependencies)))
summary.add_row("Outdated Packages", str(result.outdated_count))
summary.add_row("Vulnerabilities", str(result.vulnerable_count))
if result.source_file:
summary.add_row("Source File", result.source_file)
self.console.print(Panel(summary, title="Dependency Scan Results", expand=False))
def _print_vulnerabilities(self, result: ScanResult) -> None:
"""Print detected vulnerabilities."""
if not result.vulnerabilities:
return
vuln_table = Table(title="Security Vulnerabilities Detected", box=box.HEAVY_EDGE)
vuln_table.add_column("Package", style="cyan")
vuln_table.add_column("Current", style="red")
vuln_table.add_column("CVE ID", style="yellow")
vuln_table.add_column("Severity", style="bold")
vuln_table.add_column("Description")
severity_colors = {
Severity.CRITICAL: "red bold",
Severity.HIGH: "orange1",
Severity.MEDIUM: "yellow",
Severity.LOW: "blue",
Severity.INFO: "white",
}
for dep, vuln in result.vulnerabilities:
severity_style = severity_colors.get(vuln.severity, "white")
vuln_table.add_row(
dep.name,
dep.current_version,
vuln.cve_id,
Text(vuln.severity.value.upper(), style=severity_style),
vuln.description,
)
self.console.print(Panel(vuln_table, title="⚠ Security Vulnerabilities", expand=False))
def _print_outdated_dependencies(self, result: ScanResult) -> None:
"""Print outdated dependencies."""
outdated = [d for d in result.dependencies if d.is_outdated]
if not outdated:
return
outdated_table = Table(title="Outdated Packages", box=box.HEAVY_EDGE)
outdated_table.add_column("Package", style="cyan")
outdated_table.add_column("Current", style="yellow")
outdated_table.add_column("Latest", style="green")
outdated_table.add_column("Update Type", style="magenta")
outdated_table.add_column("Category", style="white")
for dep in outdated:
update_type = ""
if dep.latest_version:
parts = dep.current_version.split(".")
latest_parts = dep.latest_version.split(".")
if latest_parts[0] > parts[0]:
update_type = "major"
elif len(latest_parts) > 1 and len(parts) > 1 and latest_parts[1] > parts[1]:
update_type = "minor"
elif len(latest_parts) > 2 and len(parts) > 2 and latest_parts[2] > parts[2]:
update_type = "patch"
outdated_table.add_row(
dep.name,
dep.current_version,
dep.latest_version or "unknown",
update_type or "-",
dep.category,
)
self.console.print(Panel(outdated_table, title="Outdated Packages", expand=False))
def print_error(self, message: str) -> None:
"""Print error message."""
self.console.print(Panel(Text(message, style="red"), title="Error", expand=False))
def print_warning(self, message: str) -> None:
"""Print warning message."""
self.console.print(Panel(Text(message, style="yellow"), title="Warning", expand=False))
def print_info(self, message: str) -> None:
"""Print info message."""
self.console.print(Text(message, style="blue"))
def get_exit_code(self, result: ScanResult) -> int:
"""Determine exit code based on results."""
from depcheck.reporters.json import JSONReporter
return JSONReporter(self.config).get_exit_code(result)