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

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. 

6 

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. 

10 

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""" 

16 

17from __future__ import annotations 

18 

19import sys 

20 

21import click 

22from rich.console import Console 

23from rich.progress import MofNCompleteColumn, Progress, SpinnerColumn, TextColumn 

24from rich.table import Table 

25 

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 

29 

30 

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'] 

38 

39 tools = resolve_tools(registry, include=include, exclude=exclude, include_host=include_host) 

40 

41 console = Console() 

42 results: list[ToolCheckResult] = [] 

43 

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)) 

53 

54 for tool in tools: 

55 progress.update(task, description=f'Checking {tool.name}') 

56 

57 current_version = None 

58 latest_version = None 

59 current_error: str | None = None 

60 latest_error: str | None = None 

61 

62 try: 

63 current_version = tool.detect_current_version() 

64 except Exception as exc: # noqa: BLE001 

65 current_error = str(exc) 

66 

67 try: 

68 latest_version = tool.detect_latest_version() 

69 except Exception as exc: # noqa: BLE001 

70 latest_error = str(exc) 

71 

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) 

83 

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') 

91 

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]' 

100 

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]' 

108 

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]' 

116 

117 table.add_row(result.tool_name, result.category, current_str, latest_str, status_str) 

118 

119 console.print() 

120 console.print(table) 

121 

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)