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