diff --git a/archinstall/applications/management.py b/archinstall/applications/management.py new file mode 100644 index 0000000000..ae0e1a2bdf --- /dev/null +++ b/archinstall/applications/management.py @@ -0,0 +1,20 @@ +from typing import TYPE_CHECKING + +from archinstall.lib.models.application import ManagementConfiguration +from archinstall.lib.output import debug + +if TYPE_CHECKING: + from archinstall.lib.installer import Installer + + +class ManagementApp: + def install( + self, + install_session: 'Installer', + management_config: ManagementConfiguration, + ) -> None: + debug(f'Installing management tools: {[t.value for t in management_config.tools]}') + + packages = [tool.value for tool in management_config.tools] + if packages: + install_session.add_additional_packages(packages) diff --git a/archinstall/lib/applications/application_handler.py b/archinstall/lib/applications/application_handler.py index e7d16058d5..fb30663c32 100644 --- a/archinstall/lib/applications/application_handler.py +++ b/archinstall/lib/applications/application_handler.py @@ -3,6 +3,7 @@ from archinstall.applications.audio import AudioApp from archinstall.applications.bluetooth import BluetoothApp from archinstall.applications.firewall import FirewallApp +from archinstall.applications.management import ManagementApp from archinstall.applications.power_management import PowerManagementApp from archinstall.applications.print_service import PrintServiceApp from archinstall.lib.models import Audio @@ -43,5 +44,11 @@ def install_applications(self, install_session: 'Installer', app_config: Applica app_config.firewall_config, ) + if app_config.management_config and app_config.management_config.tools: + ManagementApp().install( + install_session, + app_config.management_config, + ) + application_handler = ApplicationHandler() diff --git a/archinstall/lib/applications/application_menu.py b/archinstall/lib/applications/application_menu.py index 7dd56e9871..0095996f23 100644 --- a/archinstall/lib/applications/application_menu.py +++ b/archinstall/lib/applications/application_menu.py @@ -9,6 +9,8 @@ BluetoothConfiguration, Firewall, FirewallConfiguration, + Management, + ManagementConfiguration, PowerManagement, PowerManagementConfiguration, PrintServiceConfiguration, @@ -78,6 +80,12 @@ def _define_menu_options(self) -> list[MenuItem]: preview_action=self._prev_firewall, key='firewall_config', ), + MenuItem( + text=tr('Management'), + action=select_management, + preview_action=self._prev_management, + key='management_config', + ), ] def _prev_power_management(self, item: MenuItem) -> str | None: @@ -116,6 +124,13 @@ def _prev_firewall(self, item: MenuItem) -> str | None: return f'{tr("Firewall")}: {config.firewall.value}' return None + def _prev_management(self, item: MenuItem) -> str | None: + if item.value is not None: + config: ManagementConfiguration = item.value + tools = ', '.join([t.value for t in config.tools]) + return f'{tr("Management")}: {tools}' + return None + def select_power_management(preset: PowerManagementConfiguration | None = None) -> PowerManagementConfiguration | None: group = MenuItemGroup.from_enum(PowerManagement) @@ -240,3 +255,30 @@ def select_firewall(preset: FirewallConfiguration | None = None) -> FirewallConf return FirewallConfiguration(firewall=result.get_value()) case ResultType.Reset: return None + + +def select_management(preset: ManagementConfiguration | None = None) -> ManagementConfiguration | None: + group = MenuItemGroup.from_enum(Management) + + header = tr('Would you like to install management tools?') + '\n' + + if preset: + group.set_selected_by_value(preset.tools) + + result = SelectMenu[Management]( + group, + header=header, + allow_skip=True, + alignment=Alignment.CENTER, + allow_reset=True, + frame=FrameProperties.min(tr('Management')), + multi=True, + ).run() + + match result.type_: + case ResultType.Skip: + return preset + case ResultType.Selection: + return ManagementConfiguration(tools=result.get_values()) + case ResultType.Reset: + return None diff --git a/archinstall/lib/global_menu.py b/archinstall/lib/global_menu.py index dbf728a2cb..3a71f8423c 100644 --- a/archinstall/lib/global_menu.py +++ b/archinstall/lib/global_menu.py @@ -346,6 +346,11 @@ def _prev_applications(self, item: MenuItem) -> str | None: output += f'{tr("Firewall")}: {firewall_config.firewall.value}' output += '\n' + if app_config.management_config and app_config.management_config.tools: + tools = ', '.join([t.value for t in app_config.management_config.tools]) + output += f'{tr("Management")}: {tools}' + output += '\n' + return output return None diff --git a/archinstall/lib/models/application.py b/archinstall/lib/models/application.py index 7ecb7a0717..088fd67d53 100644 --- a/archinstall/lib/models/application.py +++ b/archinstall/lib/models/application.py @@ -38,6 +38,16 @@ class FirewallConfigSerialization(TypedDict): firewall: str +class Management(StrEnum): + MAN = 'man-db' + PACMAN_CONTRIB = 'pacman-contrib' + REFLECTOR = 'reflector' + + +class ManagementConfigSerialization(TypedDict): + tools: list[str] + + class ZramAlgorithm(StrEnum): ZSTD = 'zstd' LZO_RLE = 'lzo-rle' @@ -52,6 +62,7 @@ class ApplicationSerialization(TypedDict): power_management_config: NotRequired[PowerManagementConfigSerialization] print_service_config: NotRequired[PrintServiceConfigSerialization] firewall_config: NotRequired[FirewallConfigSerialization] + management_config: NotRequired[ManagementConfigSerialization] @dataclass @@ -126,6 +137,22 @@ def parse_arg(arg: dict[str, Any]) -> 'FirewallConfiguration': ) +@dataclass +class ManagementConfiguration: + tools: list[Management] + + def json(self) -> ManagementConfigSerialization: + return { + 'tools': [t.value for t in self.tools], + } + + @staticmethod + def parse_arg(arg: ManagementConfigSerialization) -> 'ManagementConfiguration': + return ManagementConfiguration( + tools=[Management(t) for t in arg['tools']], + ) + + @dataclass(frozen=True) class ZramConfiguration: enabled: bool @@ -148,6 +175,7 @@ class ApplicationConfiguration: power_management_config: PowerManagementConfiguration | None = None print_service_config: PrintServiceConfiguration | None = None firewall_config: FirewallConfiguration | None = None + management_config: ManagementConfiguration | None = None @staticmethod def parse_arg( @@ -175,6 +203,9 @@ def parse_arg( if args and (firewall_config := args.get('firewall_config')) is not None: app_config.firewall_config = FirewallConfiguration.parse_arg(firewall_config) + if args and (management_config := args.get('management_config')) is not None: + app_config.management_config = ManagementConfiguration.parse_arg(management_config) + return app_config def json(self) -> ApplicationSerialization: @@ -195,4 +226,7 @@ def json(self) -> ApplicationSerialization: if self.firewall_config: config['firewall_config'] = self.firewall_config.json() + if self.management_config: + config['management_config'] = self.management_config.json() + return config