Coverage for src/secchi/workflows/dashboard.py: 61%
49 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-08-14 23:28 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-08-14 23:28 +0000
1"""Dashboard project-resolution workflow."""
3from dataclasses import dataclass
4from pathlib import Path
6from secchi.config import find_config, load_project, load_projects
7from secchi.diagnostics import DiagnosticLog
8from secchi.errors import ConfigError, PackageNotFoundError
9from secchi.models import PackageRef, Project
10from secchi.services.resolver import parse_package_spec, resolve_package
13@dataclass(frozen=True)
14class DashboardRequest:
15 project: Project
16 config_path: Path
17 workspace: list[Project] | None
18 refresh: bool
19 security_refresh: bool
22def _workspace_project(config_path: Path) -> Project:
23 projects = load_projects(config_path)
24 refs: list[PackageRef] = []
25 for project in projects:
26 refs.extend(
27 PackageRef(
28 ref.name,
29 ref.registry,
30 ref.favorite or project.favorite,
31 project.name,
32 )
33 for ref in project.packages
34 )
35 return Project(
36 name="Workspace", description="Configured Secchi workspace", packages=refs
37 )
40async def run(
41 *,
42 package: str | None = None,
43 registry: str | None = None,
44 project_name: str | None = None,
45 config: str | None = None,
46 refresh: bool = False,
47 security_refresh: bool = False,
48 diagnostics: DiagnosticLog | None = None,
49) -> DashboardRequest:
50 config_path = find_config(config)
51 workspace: list[Project] | None = None
52 if package:
53 ref = parse_package_spec(package, registry)
54 configured = None
55 if config_path and registry is None:
56 for candidate in load_projects(config_path):
57 if candidate.name.lower() == package.lower() or any(
58 item.name.lower() == package.lower() for item in candidate.packages
59 ):
60 configured = candidate
61 break
62 if configured is not None:
63 project = configured
64 elif registry is None:
65 refs = await resolve_package(package, diagnostics=diagnostics)
66 if not refs:
67 raise PackageNotFoundError(
68 f"No exact package named '{package}' was found across registries."
69 )
70 project = Project(name=ref.name, packages=refs)
71 else:
72 project = Project(name=ref.name, packages=[ref])
73 config_path = config_path or (Path.cwd() / "secchi.toml")
74 else:
75 if not config_path:
76 raise ConfigError(
77 "No config found. Use 'secchi dashboard PACKAGE' or create secchi.toml."
78 )
79 if project_name:
80 project = load_project(config_path, project_name)
81 else:
82 workspace = load_projects(config_path)
83 project = _workspace_project(config_path)
84 if not project.packages:
85 raise ConfigError("The selected workspace has no packages.")
86 return DashboardRequest(
87 project,
88 config_path,
89 workspace if not package else None,
90 refresh,
91 security_refresh,
92 )