Coverage for src/mafw/devtools/cli/toolchain/check_cmd.py: 100%
64 statements
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-26 09:13 +0000
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-26 09:13 +0000
1# Copyright 2026 European Union
2# Author: Bulgheroni Antonio (antonio.bulgheroni@ec.europa.eu)
3# SPDX-License-Identifier: EUPL-1.2
4"""
5Check subcommand for the toolchain command group.
7This module implements the ``devtools toolchain check`` CLI command, which
8iterates over applicable tools, detects their current and latest versions,
9and displays a Rich table summarising each tool's synchronisation status.
11A Rich progress spinner provides visual feedback during version detection
12(which may involve PyPI queries or subprocess calls). The command exits
13with code 0 if all tools are in sync, or code 1 if any tool is out of
14sync or encountered a detection error.
15"""
17from __future__ import annotations
19import sys
21import click
22from rich.console import Console
23from rich.progress import MofNCompleteColumn, Progress, SpinnerColumn, TextColumn
24from rich.table import Table
26from mafw.devtools.cli.toolchain._group import common_options, toolchain
27from mafw.devtools.toolchain.base import ToolCheckResult
28from mafw.devtools.toolchain.filtering import resolve_tools
31@toolchain.command(name='check')
32@common_options
33@click.pass_context
34def check(ctx: click.Context, include: tuple[str, ...], exclude: tuple[str, ...]) -> None:
35 """Check version synchronisation status of toolchain tools."""
36 registry = ctx.obj['registry']
37 include_host: bool = ctx.obj['include_host']
39 tools = resolve_tools(registry, include=include, exclude=exclude, include_host=include_host)
41 console = Console()
42 results: list[ToolCheckResult] = []
44 # Detect versions for each tool with a progress spinner for user feedback.
45 with Progress(
46 SpinnerColumn(),
47 TextColumn('[progress.description]{task.description}'),
48 MofNCompleteColumn(),
49 console=console,
50 transient=True,
51 ) as progress:
52 task = progress.add_task('Checking tools', total=len(tools))
54 for tool in tools:
55 progress.update(task, description=f'Checking {tool.name}')
57 current_version = None
58 latest_version = None
59 current_error: str | None = None
60 latest_error: str | None = None
62 try:
63 current_version = tool.detect_current_version()
64 except Exception as exc: # noqa: BLE001
65 current_error = str(exc)
67 try:
68 latest_version = tool.detect_latest_version()
69 except Exception as exc: # noqa: BLE001
70 latest_error = str(exc)
72 results.append(
73 ToolCheckResult(
74 tool_name=tool.name,
75 category=tool.category,
76 current_version=current_version,
77 latest_version=latest_version,
78 current_error=current_error,
79 latest_error=latest_error,
80 )
81 )
82 progress.advance(task)
84 # Build the Rich table with results.
85 table = Table(title='Toolchain Version Check', show_header=True, header_style='bold')
86 table.add_column('Tool', style='cyan')
87 table.add_column('Category')
88 table.add_column('Current')
89 table.add_column('Latest')
90 table.add_column('Status')
92 for result in results:
93 # Format current version column (include error details for debugging).
94 if result.current_error:
95 current_str = f'[red]⚠ error[/red]: {result.current_error}'
96 elif result.current_version is not None:
97 current_str = str(result.current_version)
98 else:
99 current_str = '[dim]N/A[/dim]'
101 # Format latest version column (include error details for debugging).
102 if result.latest_error:
103 latest_str = f'[red]⚠ error[/red]: {result.latest_error}'
104 elif result.latest_version is not None:
105 latest_str = str(result.latest_version)
106 else:
107 latest_str = '[dim]N/A[/dim]'
109 # Format status column with coloured indicators.
110 if result.in_sync:
111 status_str = '[green]✓ synced[/green]'
112 elif result.current_error or result.latest_error:
113 status_str = '[red]! error[/red]'
114 else:
115 status_str = '[red]✗ outdated[/red]'
117 table.add_row(result.tool_name, result.category, current_str, latest_str, status_str)
119 console.print()
120 console.print(table)
122 # Determine exit code: non-zero if any tool is out of sync or errored.
123 has_issues = any(not r.in_sync for r in results)
124 if has_issues:
125 sys.exit(1)