Coverage for src/secchi/workflows/report.py: 42%
33 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-08-04 22:15 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-08-04 22:15 +0000
1"""Package and project report workflow."""
3from dataclasses import dataclass
4from pathlib import Path
6from secchi.config import find_config, load_project
7from secchi.errors import ConfigError, ReportError
8from secchi.renderers.reports import (
9 build_project_report,
10 default_report_path,
11 render_project_report,
12 render_report,
13)
14from secchi.services.intelligence import PackageIntelligenceService
15from secchi.services.resolver import parse_package_spec
16from secchi.workflows.common import require_package
19@dataclass(frozen=True)
20class ReportOutput:
21 content: str
22 format_name: str
23 target: Path
26async def run(
27 *,
28 package: str | None = None,
29 project_name: str | None = None,
30 config: str | None = None,
31 registry: str | None = None,
32 format_name: str = "json",
33 output: str | None = None,
34 refresh: bool = False,
35 service: PackageIntelligenceService | None = None,
36) -> ReportOutput:
37 normalized_format = "md" if format_name == "markdown" else format_name
38 if project_name:
39 config_path = find_config(config)
40 if not config_path:
41 raise ConfigError("No config found for project report.")
42 project = load_project(config_path, project_name)
43 pipeline = service or PackageIntelligenceService()
44 intelligence = await pipeline.fetch_project(
45 project.packages, force_refresh=refresh
46 )
47 project_report = build_project_report(project, intelligence.results)
48 content = render_project_report(normalized_format, project_report)
49 subject = project.title or project.name
50 target = (
51 Path(output)
52 if output and output != "-"
53 else default_report_path(subject, normalized_format, project=True)
54 )
55 else:
56 if not package:
57 raise ReportError(
58 "Provide a package name or --project PROJECT for a report."
59 )
60 ref = parse_package_spec(package, registry)
61 result = (
62 await require_package(ref, refresh, service=service)
63 if service is not None
64 else await require_package(ref, refresh)
65 )
66 content = render_report(
67 normalized_format,
68 result.info,
69 result.derived,
70 ref,
71 ref.name,
72 result.warnings,
73 )
74 target = (
75 Path(output)
76 if output and output != "-"
77 else default_report_path(ref.name, normalized_format)
78 )
79 return ReportOutput(content, normalized_format, target)