From 6c9eab9a2623e797651e56bd59f7d012df0a1836 Mon Sep 17 00:00:00 2001 From: H4L0 Date: Tue, 12 May 2026 13:32:04 +0200 Subject: [PATCH 1/4] refactor menu and config to be more logical --- archinstall/lib/args.py | 35 +++--- archinstall/lib/global_menu.py | 71 ++++------- archinstall/lib/installer.py | 25 ++-- archinstall/lib/mirror/mirror_menu.py | 104 ++++++++++++++-- archinstall/lib/models/__init__.py | 4 +- archinstall/lib/models/mirrors.py | 22 +++- archinstall/lib/models/pacman.py | 50 -------- archinstall/lib/pacman/config.py | 2 +- archinstall/lib/pacman/pacman_menu.py | 117 ------------------ archinstall/scripts/guided.py | 10 +- docs/cli_parameters/config/config_options.csv | 2 +- docs/installing/guided.rst | 2 +- examples/config-sample.json | 2 +- tests/conftest.py | 4 +- tests/data/test_config.json | 2 +- ...son => test_deprecated_pacman_config.json} | 2 +- tests/test_args.py | 15 ++- tests/test_configuration_output.py | 4 +- 18 files changed, 187 insertions(+), 286 deletions(-) delete mode 100644 archinstall/lib/models/pacman.py delete mode 100644 archinstall/lib/pacman/pacman_menu.py rename tests/data/{test_deprecated_mirror_config.json => test_deprecated_pacman_config.json} (94%) diff --git a/archinstall/lib/args.py b/archinstall/lib/args.py index efe9baa2f3..1f56d6bdd3 100644 --- a/archinstall/lib/args.py +++ b/archinstall/lib/args.py @@ -21,11 +21,10 @@ from archinstall.lib.models.config import SubConfig from archinstall.lib.models.device import DiskEncryption, DiskLayoutConfiguration from archinstall.lib.models.locale import LocaleConfiguration -from archinstall.lib.models.mirrors import MirrorConfiguration +from archinstall.lib.models.mirrors import PacmanConfiguration from archinstall.lib.models.network import NetworkConfiguration from archinstall.lib.models.package_types import DEFAULT_KERNEL from archinstall.lib.models.packages import Repository -from archinstall.lib.models.pacman import PacmanConfiguration from archinstall.lib.models.profile import ProfileConfiguration from archinstall.lib.models.users import Password, User, UserSerialization from archinstall.lib.output import debug, error, logger, warn @@ -66,7 +65,6 @@ class ArchConfigType(StrEnum): ARCHINSTALL_LANGUAGE = 'archinstall_language' DISK_CONFIG = 'disk_config' PROFILE_CONFIG = 'profile_config' - MIRROR_CONFIG = 'mirror_config' NETWORK_CONFIG = 'network_config' BOOTLOADER_CONFIG = 'bootloader_config' APP_CONFIG = 'app_config' @@ -98,8 +96,6 @@ def text(self) -> str: return tr('Disk configuration') case ArchConfigType.PROFILE_CONFIG: return tr('Profile') - case ArchConfigType.MIRROR_CONFIG: - return tr('Mirrors and repositories') case ArchConfigType.NETWORK_CONFIG: return tr('Network') case ArchConfigType.BOOTLOADER_CONFIG: @@ -123,7 +119,7 @@ def text(self) -> str: case ArchConfigType.PACKAGES: return tr('Additional packages') case ArchConfigType.PACMAN_CONFIG: - return tr('Pacman') + return tr('Pacman configuration') case ArchConfigType.CUSTOM_COMMANDS: return tr('Custom commands') case ArchConfigType.USERS: @@ -142,7 +138,7 @@ class ArchConfig: archinstall_language: Language = field(default_factory=lambda: translation_handler.get_language_by_abbr('en')) disk_config: DiskLayoutConfiguration | None = None profile_config: ProfileConfiguration | None = None - mirror_config: MirrorConfiguration | None = None + pacman_config: PacmanConfiguration | None = None network_config: NetworkConfiguration | None = None bootloader_config: BootloaderConfiguration | None = None app_config: ApplicationConfiguration | None = None @@ -152,7 +148,6 @@ class ArchConfig: kernels: list[str] = field(default_factory=lambda: [DEFAULT_KERNEL.value]) ntp: bool = True packages: list[str] = field(default_factory=list) - pacman_config: PacmanConfiguration = field(default_factory=PacmanConfiguration.default) timezone: str = 'UTC' services: list[str] = field(default_factory=list) custom_commands: list[str] = field(default_factory=list) @@ -203,12 +198,10 @@ def plain_cfg(self) -> dict[ArchConfigType, str | list[str] | bool]: } def sub_cfg(self) -> dict[ArchConfigType, SubConfig]: - cfg: dict[ArchConfigType, SubConfig] = { - ArchConfigType.PACMAN_CONFIG: self.pacman_config, - } + cfg: dict[ArchConfigType, SubConfig] = {} - if self.mirror_config: - cfg[ArchConfigType.MIRROR_CONFIG] = self.mirror_config + if self.pacman_config: + cfg[ArchConfigType.PACMAN_CONFIG] = self.pacman_config if self.bootloader_config: cfg[ArchConfigType.BOOTLOADER_CONFIG] = self.bootloader_config @@ -271,16 +264,21 @@ def from_config(cls, args_config: dict[str, Any], args: Arguments) -> Self: if profile_config := args_config.get('profile_config', None): arch_config.profile_config = ProfileConfiguration.parse_arg(profile_config) - if mirror_config := args_config.get('mirror_config', None): + if pacman_config := args_config.get('pacman_config', None): backwards_compatible_repo = [] if additional_repositories := args_config.get('additional-repositories', []): backwards_compatible_repo = [Repository(r) for r in additional_repositories] - arch_config.mirror_config = MirrorConfiguration.parse_args( - mirror_config, + arch_config.pacman_config = PacmanConfiguration.parse_args( + pacman_config, backwards_compatible_repo, ) + if parallel_downloads := args_config.get('parallel_downloads', 0): + if arch_config.pacman_config is None: + arch_config.pacman_config = PacmanConfiguration() + arch_config.pacman_config.parallel_downloads = int(parallel_downloads) + if net_config := args_config.get('network_config', None): arch_config.network_config = NetworkConfiguration.parse_arg(net_config) @@ -315,11 +313,6 @@ def from_config(cls, args_config: dict[str, Any], args: Arguments) -> Self: if packages := args_config.get('packages', []): arch_config.packages = packages - if pacman_config := args_config.get('pacman_config', None): - arch_config.pacman_config = PacmanConfiguration.parse_arg(pacman_config) - elif parallel_downloads := args_config.get('parallel_downloads', 0): - arch_config.pacman_config = PacmanConfiguration(parallel_downloads=int(parallel_downloads)) - swap_arg = args_config.get('swap') if swap_arg is not None: arch_config.swap = ZramConfiguration.parse_arg(swap_arg) diff --git a/archinstall/lib/global_menu.py b/archinstall/lib/global_menu.py index eac936bdd0..292e6cda20 100644 --- a/archinstall/lib/global_menu.py +++ b/archinstall/lib/global_menu.py @@ -14,23 +14,21 @@ from archinstall.lib.locale.locale_menu import LocaleMenu from archinstall.lib.menu.abstract_menu import AbstractMenu, SpecialMenuKey from archinstall.lib.mirror.mirror_handler import MirrorListHandler -from archinstall.lib.mirror.mirror_menu import MirrorMenu +from archinstall.lib.mirror.mirror_menu import PacmanMenu from archinstall.lib.models.application import ApplicationConfiguration, ZramConfiguration from archinstall.lib.models.authentication import AuthenticationConfiguration from archinstall.lib.models.bootloader import Bootloader, BootloaderConfiguration from archinstall.lib.models.device import DiskLayoutConfiguration, DiskLayoutType, PartitionModification from archinstall.lib.models.locale import LocaleConfiguration -from archinstall.lib.models.mirrors import MirrorConfiguration +from archinstall.lib.models.mirrors import PacmanConfiguration from archinstall.lib.models.network import NetworkConfiguration, NicType from archinstall.lib.models.package_types import DEFAULT_KERNEL from archinstall.lib.models.packages import Repository -from archinstall.lib.models.pacman import PacmanConfiguration from archinstall.lib.models.profile import ProfileConfiguration from archinstall.lib.network.network_menu import select_network from archinstall.lib.output import FormattedOutput from archinstall.lib.packages.packages import list_available_packages, select_additional_packages from archinstall.lib.pacman.config import PacmanConfig -from archinstall.lib.pacman.pacman_menu import PacmanMenu from archinstall.lib.translationhandler import Language, tr, translation_handler from archinstall.tui.components import tui from archinstall.tui.menu_item import MenuItem, MenuItemGroup @@ -76,10 +74,10 @@ def _get_menu_options(self) -> list[MenuItem]: key='locale_config', ), MenuItem( - text=tr('Mirrors and repositories'), - action=self._mirror_configuration, - preview_action=self._prev_mirror_config, - key='mirror_config', + text=tr('Pacman configuration'), + action=self._pacman_configuration, + preview_action=self._prev_pacman_config, + key='pacman_config', ), MenuItem( text=tr('Disk configuration'), @@ -143,13 +141,6 @@ def _get_menu_options(self) -> list[MenuItem]: preview_action=self._prev_network_config, key='network_config', ), - MenuItem( - text=tr('Pacman'), - action=self._pacman_configuration, - value=PacmanConfiguration.default(), - preview_action=self._prev_pacman_config, - key='pacman_config', - ), MenuItem( text=tr('Additional packages'), action=self._select_additional_packages, @@ -419,19 +410,6 @@ def _prev_hostname(self, item: MenuItem) -> str | None: return f'{tr("Hostname")}: {item.value}' return None - async def _pacman_configuration(self, preset: PacmanConfiguration) -> PacmanConfiguration | None: - return await PacmanMenu(preset, advanced=self._advanced).show() - - def _prev_pacman_config(self, item: MenuItem) -> str | None: - if not item.value: - return None - config: PacmanConfiguration = item.value - output = '' - if self._advanced: - output += '{}: {}\n'.format(tr('Parallel Downloads'), config.parallel_downloads) - output += '{}: {}'.format(tr('Color'), config.color) - return output - def _prev_kernel(self, item: MenuItem) -> str | None: if item.value: kernel = ', '.join(item.value) @@ -555,7 +533,7 @@ async def _select_profile(self, current_profile: ProfileConfiguration | None) -> return profile_config async def _select_additional_packages(self, preset: list[str]) -> list[str]: - config: MirrorConfiguration | None = self._item_group.find_by_key('mirror_config').value + config: PacmanConfiguration | None = self._item_group.find_by_key('pacman_config').value repositories: set[Repository] = set() if config: @@ -568,51 +546,54 @@ async def _select_additional_packages(self, preset: list[str]) -> list[str]: return packages - async def _mirror_configuration(self, preset: MirrorConfiguration | None = None) -> MirrorConfiguration | None: + async def _pacman_configuration(self, preset: PacmanConfiguration | None = None) -> PacmanConfiguration | None: if self._mirror_list_handler is None: self._mirror_list_handler = MirrorListHandler() - mirror_configuration = await MirrorMenu(self._mirror_list_handler, preset=preset).run() + pacman_configuration = await PacmanMenu(self._mirror_list_handler, preset=preset).run() - if mirror_configuration and mirror_configuration.optional_repositories: + if pacman_configuration and pacman_configuration.optional_repositories: # reset the package list cache in case the repository selection has changed list_available_packages.cache_clear() # enable the repositories in the config pacman_config = PacmanConfig(None) - pacman_config.enable(mirror_configuration.optional_repositories) + pacman_config.enable(pacman_configuration.optional_repositories) pacman_config.apply() - return mirror_configuration + return pacman_configuration - def _prev_mirror_config(self, item: MenuItem) -> str | None: + def _prev_pacman_config(self, item: MenuItem) -> str | None: if not item.value: return None - mirror_config: MirrorConfiguration = item.value + pacman_config: PacmanConfiguration = item.value output = '' - if mirror_config.mirror_regions: + if pacman_config.mirror_regions: title = tr('Selected mirror regions') divider = '-' * len(title) - regions = mirror_config.region_names + regions = pacman_config.region_names output += f'{title}\n{divider}\n{regions}\n\n' - if mirror_config.custom_servers: + if pacman_config.custom_servers: title = tr('Custom servers') divider = '-' * len(title) - servers = mirror_config.custom_server_urls + servers = pacman_config.custom_server_urls output += f'{title}\n{divider}\n{servers}\n\n' - if mirror_config.optional_repositories: + if pacman_config.optional_repositories: title = tr('Optional repositories') divider = '-' * len(title) - repos = ', '.join(r.value for r in mirror_config.optional_repositories) + repos = ', '.join(r.value for r in pacman_config.optional_repositories) output += f'{title}\n{divider}\n{repos}\n\n' - if mirror_config.custom_repositories: + if pacman_config.custom_repositories: title = tr('Custom repositories') - table = FormattedOutput.as_table(mirror_config.custom_repositories) - output += f'{title}:\n\n{table}' + table = FormattedOutput.as_table(pacman_config.custom_repositories) + output += f'{title}:\n\n{table}\n\n' + + output += '{}: {}\n'.format(tr('Parallel Downloads'), pacman_config.parallel_downloads) + output += '{}: {}'.format(tr('Color'), pacman_config.color) return output.strip() diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 801eaee22b..309cffa138 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -46,11 +46,10 @@ Unit, ) from archinstall.lib.models.locale import LocaleConfiguration -from archinstall.lib.models.mirrors import MirrorConfiguration +from archinstall.lib.models.mirrors import PacmanConfiguration from archinstall.lib.models.network import Nic from archinstall.lib.models.package_types import DEFAULT_KERNEL, Kernel from archinstall.lib.models.packages import Repository -from archinstall.lib.models.pacman import PacmanConfiguration from archinstall.lib.models.users import User from archinstall.lib.output import debug, error, info, log, logger, warn from archinstall.lib.packages.packages import installed_package @@ -553,14 +552,14 @@ def post_install_check(self, *args: str, **kwargs: str) -> list[str]: def set_mirrors( self, mirror_list_handler: MirrorListHandler, - mirror_config: MirrorConfiguration, + pacman_config: PacmanConfiguration, on_target: bool = False, ) -> None: """ Set the mirror configuration for the installation. - :param mirror_config: The mirror configuration to use. - :type mirror_config: MirrorConfiguration + :param pacman_config: The mirror configuration to use. + :type pacman_config: PacmanConfiguration :on_target: Whether to set the mirrors on the target system or the live system. :param on_target: bool @@ -569,29 +568,29 @@ def set_mirrors( for plugin in plugins.values(): if hasattr(plugin, 'on_mirrors'): - if result := plugin.on_mirrors(mirror_config): - mirror_config = result + if result := plugin.on_mirrors(pacman_config): + pacman_config = result if on_target: mirrorlist_config = self.target / MIRRORLIST.relative_to_root() - pacman_config = self.target / PACMAN_CONF.relative_to_root() + pacman_conf_path = self.target / PACMAN_CONF.relative_to_root() else: mirrorlist_config = MIRRORLIST - pacman_config = PACMAN_CONF + pacman_conf_path = PACMAN_CONF - repositories_config = mirror_config.repositories_config() + repositories_config = pacman_config.repositories_config() if repositories_config: debug(f'Pacman config: {repositories_config}') - with open(pacman_config, 'a') as fp: + with open(pacman_conf_path, 'a') as fp: fp.write(repositories_config) - regions_config = mirror_config.regions_config(mirror_list_handler, speed_sort=True) + regions_config = pacman_config.regions_config(mirror_list_handler, speed_sort=True) if regions_config: debug(f'Mirrorlist:\n{regions_config}') mirrorlist_config.write_text(regions_config) - custom_servers = mirror_config.custom_servers_config() + custom_servers = pacman_config.custom_servers_config() if custom_servers: debug(f'Custom servers:\n{custom_servers}') diff --git a/archinstall/lib/mirror/mirror_menu.py b/archinstall/lib/mirror/mirror_menu.py index 5158bffdec..a6ac74816f 100644 --- a/archinstall/lib/mirror/mirror_menu.py +++ b/archinstall/lib/mirror/mirror_menu.py @@ -1,19 +1,20 @@ from typing import override from archinstall.lib.menu.abstract_menu import AbstractSubMenu -from archinstall.lib.menu.helpers import Input, Loading, Selection +from archinstall.lib.menu.helpers import Confirmation, Input, Loading, Selection from archinstall.lib.menu.list_manager import ListManager from archinstall.lib.mirror.mirror_handler import MirrorListHandler from archinstall.lib.models.mirrors import ( CustomRepository, CustomServer, - MirrorConfiguration, MirrorRegion, + PacmanConfiguration, SignCheck, SignOption, ) from archinstall.lib.models.packages import Repository from archinstall.lib.output import FormattedOutput +from archinstall.lib.pathnames import PACMAN_CONF from archinstall.lib.translationhandler import tr from archinstall.tui.menu_item import MenuItem, MenuItemGroup from archinstall.tui.result import ResultType @@ -201,16 +202,16 @@ async def _add_custom_server(self, preset: CustomServer | None = None) -> Custom return None -class MirrorMenu(AbstractSubMenu[MirrorConfiguration]): +class PacmanMenu(AbstractSubMenu[PacmanConfiguration]): def __init__( self, mirror_list_handler: MirrorListHandler, - preset: MirrorConfiguration | None = None, + preset: PacmanConfiguration | None = None, ): if preset: - self._mirror_config = preset + self._pacman_config = preset else: - self._mirror_config = MirrorConfiguration() + self._pacman_config = PacmanConfiguration() self._mirror_list_handler = mirror_list_handler @@ -219,7 +220,7 @@ def __init__( super().__init__( self._item_group, - config=self._mirror_config, + config=self._pacman_config, allow_reset=True, ) @@ -228,14 +229,14 @@ def _define_menu_options(self) -> list[MenuItem]: MenuItem( text=tr('Select regions'), action=lambda x: select_mirror_regions(self._mirror_list_handler, x), - value=self._mirror_config.mirror_regions, + value=self._pacman_config.mirror_regions, preview_action=self._prev_regions, key='mirror_regions', ), MenuItem( text=tr('Add custom servers'), action=add_custom_mirror_servers, - value=self._mirror_config.custom_servers, + value=self._pacman_config.custom_servers, preview_action=self._prev_custom_servers, key='custom_servers', ), @@ -249,10 +250,24 @@ def _define_menu_options(self) -> list[MenuItem]: MenuItem( text=tr('Add custom repository'), action=select_custom_mirror, - value=self._mirror_config.custom_repositories, + value=self._pacman_config.custom_repositories, preview_action=self._prev_custom_mirror, key='custom_repositories', ), + MenuItem( + text=tr('Parallel Downloads'), + action=select_parallel_downloads, + value=self._pacman_config.parallel_downloads, + preview_action=lambda item: str(item.get_value()), + key='parallel_downloads', + ), + MenuItem( + text=tr('Color'), + action=select_color, + value=self._pacman_config.color, + preview_action=lambda item: str(item.get_value()), + key='color', + ), ] def _prev_regions(self, item: MenuItem) -> str: @@ -293,8 +308,73 @@ def _prev_custom_servers(self, item: MenuItem) -> str | None: return output.strip() @override - async def show(self) -> MirrorConfiguration | None: - return await super().show() + async def show(self) -> PacmanConfiguration | None: + config = await super().show() + + if config is not None: + _apply_to_live(config.parallel_downloads) + + return config + + +def _apply_to_live(parallel_downloads: int) -> None: + """Apply ParallelDownloads to live system pacman.conf for faster installation.""" + with PACMAN_CONF.open() as f: + pacman_conf = f.read().split('\n') + + with PACMAN_CONF.open('w') as fwrite: + for line in pacman_conf: + if 'ParallelDownloads' in line: + fwrite.write(f'ParallelDownloads = {parallel_downloads}\n') + else: + fwrite.write(f'{line}\n') + + +async def select_parallel_downloads(preset: int = 5) -> int | None: + max_recommended = 10 + + header = tr('Enter the number of parallel downloads (1-{})').format(max_recommended) + + def validator(s: str) -> str | None: + try: + value = int(s) + if 1 <= value <= max_recommended: + return None + return tr('Value must be between 1 and {}').format(max_recommended) + except Exception: + return tr('Please enter a valid number') + + result = await Input( + header=header, + allow_skip=True, + allow_reset=True, + validator_callback=validator, + default_value=str(preset), + ).show() + + match result.type_: + case ResultType.Skip: + return preset + case ResultType.Reset: + return 5 + case ResultType.Selection: + return int(result.get_value()) + + +async def select_color(preset: bool = True) -> bool | None: + result = await Confirmation( + header=tr('Enable colored output for pacman'), + preset=preset, + allow_skip=True, + ).show() + + match result.type_: + case ResultType.Skip: + return preset + case ResultType.Reset: + return True + case ResultType.Selection: + return result.get_value() async def select_mirror_regions( diff --git a/archinstall/lib/models/__init__.py b/archinstall/lib/models/__init__.py index 5f808c96f2..156f21ac1c 100644 --- a/archinstall/lib/models/__init__.py +++ b/archinstall/lib/models/__init__.py @@ -27,7 +27,7 @@ _DeviceInfo, ) from archinstall.lib.models.locale import LocaleConfiguration -from archinstall.lib.models.mirrors import CustomRepository, MirrorConfiguration, MirrorRegion +from archinstall.lib.models.mirrors import CustomRepository, MirrorRegion, PacmanConfiguration from archinstall.lib.models.network import NetworkConfiguration, Nic, NicType from archinstall.lib.models.packages import LocalPackage, PackageSearch, PackageSearchResult, Repository from archinstall.lib.models.profile import ProfileConfiguration @@ -56,7 +56,6 @@ 'LvmLayoutType', 'LvmVolume', 'LvmVolumeGroup', - 'MirrorConfiguration', 'MirrorRegion', 'ModificationStatus', 'NetworkConfiguration', @@ -64,6 +63,7 @@ 'NicType', 'PackageSearch', 'PackageSearchResult', + 'PacmanConfiguration', 'PartitionFlag', 'PartitionModification', 'PartitionTable', diff --git a/archinstall/lib/models/mirrors.py b/archinstall/lib/models/mirrors.py index 3ded2a9a7d..de359df813 100644 --- a/archinstall/lib/models/mirrors.py +++ b/archinstall/lib/models/mirrors.py @@ -230,19 +230,23 @@ def parse_args(cls, args: list[dict[str, str]]) -> list[Self]: return configs -class _MirrorConfigurationSerialization(TypedDict): +class _PacmanConfigurationSerialization(TypedDict): mirror_regions: dict[str, list[str]] custom_servers: list[CustomServer] optional_repositories: list[str] custom_repositories: list[_CustomRepositorySerialization] + parallel_downloads: int + color: bool @dataclass -class MirrorConfiguration(SubConfig): +class PacmanConfiguration(SubConfig): mirror_regions: list[MirrorRegion] = field(default_factory=list) custom_servers: list[CustomServer] = field(default_factory=list) optional_repositories: list[Repository] = field(default_factory=list) custom_repositories: list[CustomRepository] = field(default_factory=list) + parallel_downloads: int = 5 + color: bool = True @property def region_names(self) -> str: @@ -253,7 +257,7 @@ def custom_server_urls(self) -> str: return '\n'.join(s.url for s in self.custom_servers) @override - def json(self) -> _MirrorConfigurationSerialization: + def json(self) -> _PacmanConfigurationSerialization: regions = {} for m in self.mirror_regions: regions.update(m.json()) @@ -263,6 +267,8 @@ def json(self) -> _MirrorConfigurationSerialization: 'custom_servers': self.custom_servers, 'optional_repositories': [r.value for r in self.optional_repositories], 'custom_repositories': [c.json() for c in self.custom_repositories], + 'parallel_downloads': self.parallel_downloads, + 'color': self.color, } @override @@ -281,6 +287,11 @@ def summary(self) -> list[str]: if self.custom_repositories: out.append(tr('Custom repositories set up')) + out.append(tr('Parallel downloads: {}').format(self.parallel_downloads)) + + if self.color: + out.append(tr('Color enabled')) + return out def custom_servers_config(self) -> str: @@ -353,4 +364,9 @@ def parse_args( if r not in config.optional_repositories: config.optional_repositories.append(r) + if 'parallel_downloads' in args: + config.parallel_downloads = int(args['parallel_downloads']) + if 'color' in args: + config.color = bool(args['color']) + return config diff --git a/archinstall/lib/models/pacman.py b/archinstall/lib/models/pacman.py deleted file mode 100644 index 004dfbc3f6..0000000000 --- a/archinstall/lib/models/pacman.py +++ /dev/null @@ -1,50 +0,0 @@ -from dataclasses import dataclass -from typing import Self, TypedDict, override - -from archinstall.lib.models.config import SubConfig -from archinstall.lib.translationhandler import tr - - -class PacmanConfigSerialization(TypedDict): - parallel_downloads: int - color: bool - - -@dataclass -class PacmanConfiguration(SubConfig): - parallel_downloads: int = 5 - color: bool = True - - @classmethod - def default(cls) -> Self: - return cls() - - @override - def json(self) -> PacmanConfigSerialization: - return { - 'parallel_downloads': self.parallel_downloads, - 'color': self.color, - } - - @override - def summary(self) -> str | None: - if self.color: - return tr('Color enabled') - return None - - def preview(self) -> str: - color_str = str(self.color) - output = '{}: {}\n'.format(tr('Parallel Downloads'), self.parallel_downloads) - output += '{}: {}'.format(tr('Color'), color_str) - return output - - @classmethod - def parse_arg(cls, args: PacmanConfigSerialization) -> Self: - config = cls.default() - - if 'parallel_downloads' in args: - config.parallel_downloads = int(args['parallel_downloads']) - if 'color' in args: - config.color = bool(args['color']) - - return config diff --git a/archinstall/lib/pacman/config.py b/archinstall/lib/pacman/config.py index 5785b31994..f6619bf8ab 100644 --- a/archinstall/lib/pacman/config.py +++ b/archinstall/lib/pacman/config.py @@ -1,8 +1,8 @@ import re from pathlib import Path +from archinstall.lib.models.mirrors import PacmanConfiguration from archinstall.lib.models.packages import Repository -from archinstall.lib.models.pacman import PacmanConfiguration from archinstall.lib.pathnames import PACMAN_CONF diff --git a/archinstall/lib/pacman/pacman_menu.py b/archinstall/lib/pacman/pacman_menu.py deleted file mode 100644 index 16ad6d2b58..0000000000 --- a/archinstall/lib/pacman/pacman_menu.py +++ /dev/null @@ -1,117 +0,0 @@ -from typing import override - -from archinstall.lib.menu.abstract_menu import AbstractSubMenu -from archinstall.lib.menu.helpers import Confirmation, Input -from archinstall.lib.models.pacman import PacmanConfiguration -from archinstall.lib.pathnames import PACMAN_CONF -from archinstall.lib.translationhandler import tr -from archinstall.tui.menu_item import MenuItem, MenuItemGroup -from archinstall.tui.result import ResultType - - -class PacmanMenu(AbstractSubMenu[PacmanConfiguration]): - def __init__( - self, - pacman_conf: PacmanConfiguration, - advanced: bool = False, - ): - self._pacman_conf = pacman_conf - self._advanced = advanced - menu_options = self._define_menu_options() - - self._item_group = MenuItemGroup(menu_options, sort_items=False, checkmarks=True) - super().__init__( - self._item_group, - config=self._pacman_conf, - allow_reset=True, - ) - - def _define_menu_options(self) -> list[MenuItem]: - return [ - MenuItem( - text=tr('Parallel Downloads'), - action=select_parallel_downloads, - value=self._pacman_conf.parallel_downloads, - preview_action=lambda item: str(item.get_value()), - key='parallel_downloads', - enabled=self._advanced, - ), - MenuItem( - text=tr('Color'), - action=select_color, - value=self._pacman_conf.color, - preview_action=lambda item: str(item.get_value()), - key='color', - ), - ] - - @override - async def show(self) -> PacmanConfiguration | None: - config = await super().show() - - if config is None: - return PacmanConfiguration.default() - - _apply_to_live(config.parallel_downloads) - - return config - - -def _apply_to_live(parallel_downloads: int) -> None: - """Apply ParallelDownloads to live system pacman.conf for faster installation.""" - with PACMAN_CONF.open() as f: - pacman_conf = f.read().split('\n') - - with PACMAN_CONF.open('w') as fwrite: - for line in pacman_conf: - if 'ParallelDownloads' in line: - fwrite.write(f'ParallelDownloads = {parallel_downloads}\n') - else: - fwrite.write(f'{line}\n') - - -async def select_parallel_downloads(preset: int = 5) -> int | None: - max_recommended = 10 - - header = tr('Enter the number of parallel downloads (1-{})').format(max_recommended) - - def validator(s: str) -> str | None: - try: - value = int(s) - if 1 <= value <= max_recommended: - return None - return tr('Value must be between 1 and {}').format(max_recommended) - except Exception: - return tr('Please enter a valid number') - - result = await Input( - header=header, - allow_skip=True, - allow_reset=True, - validator_callback=validator, - default_value=str(preset), - ).show() - - match result.type_: - case ResultType.Skip: - return preset - case ResultType.Reset: - return 5 - case ResultType.Selection: - return int(result.get_value()) - - -async def select_color(preset: bool = True) -> bool | None: - result = await Confirmation( - header=tr('Enable colored output for pacman'), - preset=preset, - allow_skip=True, - ).show() - - match result.type_: - case ResultType.Skip: - return preset - case ResultType.Reset: - return True - case ResultType.Selection: - return result.get_value() diff --git a/archinstall/scripts/guided.py b/archinstall/scripts/guided.py index aab02cfd4d..8c506a8992 100644 --- a/archinstall/scripts/guided.py +++ b/archinstall/scripts/guided.py @@ -73,7 +73,7 @@ def perform_installation( disk_config = config.disk_config run_mkinitcpio = not config.bootloader_config or not config.bootloader_config.uki locale_config = config.locale_config - optional_repositories = config.mirror_config.optional_repositories if config.mirror_config else [] + optional_repositories = config.pacman_config.optional_repositories if config.pacman_config else [] mountpoint = disk_config.mountpoint if disk_config.mountpoint else mountpoint with Installer( @@ -97,8 +97,8 @@ def perform_installation( # generate encryption key files for the mounted luks devices installation.generate_key_files() - if mirror_config := config.mirror_config: - installation.set_mirrors(mirror_list_handler, mirror_config, on_target=False) + if pacman_config := config.pacman_config: + installation.set_mirrors(mirror_list_handler, pacman_config, on_target=False) installation.minimal_installation( optional_repositories=optional_repositories, @@ -108,8 +108,8 @@ def perform_installation( pacman_config=config.pacman_config, ) - if mirror_config := config.mirror_config: - installation.set_mirrors(mirror_list_handler, mirror_config, on_target=True) + if pacman_config := config.pacman_config: + installation.set_mirrors(mirror_list_handler, pacman_config, on_target=True) if config.swap and config.swap.enabled: installation.setup_swap(algo=config.swap.algorithm) diff --git a/docs/cli_parameters/config/config_options.csv b/docs/cli_parameters/config/config_options.csv index a902d1d20e..1e05cbce5a 100644 --- a/docs/cli_parameters/config/config_options.csv +++ b/docs/cli_parameters/config/config_options.csv @@ -10,7 +10,7 @@ hostname,``str``,A string defining your machines hostname on the network *(defau kernels,[ `linux `_!, `linux-hardened `_!, `linux-lts `_!, `linux-rt `_!, `linux-rt-lts `_!, `linux-zen `_ ],Defines which kernels should be installed and setup in the boot loader options,Yes custom_commands,*Read more under* :ref:`custom commands`,Custom commands that will be run post-install chrooted inside the installed system,No locale_config,{kb_layout: `lang `__!, sys_enc: `Character encoding `_!, sys_lang: `locale `_},Defines the keyboard key map!, system encoding and system locale,No -mirror_config,{custom_mirrors: [ https://... ]!, mirror_regions: { "Worldwide": [ "https://geo.mirror.pkgbuild.com/$repo/os/$arch" ] } },Sets various mirrors *(defaults to ISO's ``/etc/pacman.d/mirrors`` if not defined)*,No +pacman_config,{custom_mirrors: [ https://... ]!, mirror_regions: { "Worldwide": [ "https://geo.mirror.pkgbuild.com/$repo/os/$arch" ] } },Sets various mirrors *(defaults to ISO's ``/etc/pacman.d/mirrors`` if not defined)*,No network_config,*`see options under Network Configuration`*,Sets which type of *(if any)* network configuration should be used,No no_pkg_lookups,``true``!, ``false``,Disabled package checking against https://archlinux.org/packages/,No ntp,``true``!, ``false``,enables or disables `NTP `_ during installation,No diff --git a/docs/installing/guided.rst b/docs/installing/guided.rst index 562d2b2980..517d3c01ab 100644 --- a/docs/installing/guided.rst +++ b/docs/installing/guided.rst @@ -192,7 +192,7 @@ The contents of :code:`https://domain.lan/config.json`: "sys_enc": "UTF-8", "sys_lang": "en_US" }, - "mirror_config": { + "pacman_config": { "custom_servers": [ { "url": "https://mymirror.com/$repo/os/$arch" diff --git a/examples/config-sample.json b/examples/config-sample.json index bc4cd2d924..19da96f486 100644 --- a/examples/config-sample.json +++ b/examples/config-sample.json @@ -91,7 +91,7 @@ "sys_enc": "UTF-8", "sys_lang": "en_US" }, - "mirror_config": { + "pacman_config": { "custom_servers": [ { "url": "https://mymirror.com/$repo/os/$arch" diff --git a/tests/conftest.py b/tests/conftest.py index 819c839716..85958fa127 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -29,8 +29,8 @@ def deprecated_creds_config() -> Path: @pytest.fixture(scope='session') -def deprecated_mirror_config() -> Path: - return Path(__file__).parent / 'data' / 'test_deprecated_mirror_config.json' +def deprecated_pacman_config() -> Path: + return Path(__file__).parent / 'data' / 'test_deprecated_pacman_config.json' @pytest.fixture(scope='session') diff --git a/tests/data/test_config.json b/tests/data/test_config.json index 618bd8e9c7..a1bc4dbf8a 100644 --- a/tests/data/test_config.json +++ b/tests/data/test_config.json @@ -138,7 +138,7 @@ "sys_enc": "UTF-8", "sys_lang": "en_US" }, - "mirror_config": { + "pacman_config": { "custom_servers": [ { "url": "https://mymirror.com/$repo/os/$arch" diff --git a/tests/data/test_deprecated_mirror_config.json b/tests/data/test_deprecated_pacman_config.json similarity index 94% rename from tests/data/test_deprecated_mirror_config.json rename to tests/data/test_deprecated_pacman_config.json index ee82a97404..b0e17135d0 100644 --- a/tests/data/test_deprecated_mirror_config.json +++ b/tests/data/test_deprecated_pacman_config.json @@ -1,6 +1,6 @@ { "additional-repositories": ["testing"], - "mirror_config": { + "pacman_config": { "custom_mirrors": [ { "name": "my_mirror", diff --git a/tests/test_args.py b/tests/test_args.py index 4cbad41bae..e6cea4378d 100644 --- a/tests/test_args.py +++ b/tests/test_args.py @@ -19,10 +19,9 @@ from archinstall.lib.models.bootloader import Bootloader, BootloaderConfiguration from archinstall.lib.models.device import DiskLayoutConfiguration, DiskLayoutType from archinstall.lib.models.locale import LocaleConfiguration -from archinstall.lib.models.mirrors import CustomRepository, CustomServer, MirrorConfiguration, MirrorRegion, SignCheck, SignOption +from archinstall.lib.models.mirrors import CustomRepository, CustomServer, MirrorRegion, PacmanConfiguration, SignCheck, SignOption from archinstall.lib.models.network import NetworkConfiguration, Nic, NicType from archinstall.lib.models.packages import Repository -from archinstall.lib.models.pacman import PacmanConfiguration from archinstall.lib.models.profile import ProfileConfiguration from archinstall.lib.models.users import Password, User from archinstall.lib.profile.profiles_handler import profile_handler @@ -193,7 +192,7 @@ def test_config_file_parsing( gfx_driver=GfxDriver.AllOpenSource, greeter=GreeterType.Lightdm, ), - mirror_config=MirrorConfiguration( + pacman_config=PacmanConfiguration( mirror_regions=[ MirrorRegion( name='Australia', @@ -210,6 +209,7 @@ def test_config_file_parsing( sign_option=SignOption.TrustAll, ), ], + parallel_downloads=66, ), network_config=NetworkConfiguration( type=NicType.MANUAL, @@ -235,7 +235,6 @@ def test_config_file_parsing( kernels=['linux-zen'], ntp=True, packages=['firefox'], - pacman_config=PacmanConfiguration(parallel_downloads=66), swap=ZramConfiguration(enabled=False), timezone='UTC', services=['service_1', 'service_2'], @@ -243,23 +242,23 @@ def test_config_file_parsing( ) -def test_deprecated_mirror_config_parsing( +def test_deprecated_pacman_config_parsing( monkeypatch: MonkeyPatch, - deprecated_mirror_config: Path, + deprecated_pacman_config: Path, ) -> None: monkeypatch.setattr( 'sys.argv', [ 'archinstall', '--config', - str(deprecated_mirror_config), + str(deprecated_pacman_config), ], ) handler = ArchConfigHandler() arch_config = handler.config - assert arch_config.mirror_config == MirrorConfiguration( + assert arch_config.pacman_config == PacmanConfiguration( mirror_regions=[ MirrorRegion( name='Australia', diff --git a/tests/test_configuration_output.py b/tests/test_configuration_output.py index 7d9f1f97cf..d1a9af2266 100644 --- a/tests/test_configuration_output.py +++ b/tests/test_configuration_output.py @@ -38,10 +38,10 @@ def test_user_config_roundtrip( result['disk_config']['device_modifications'] = expected['disk_config']['device_modifications'] assert json.dumps( - result['mirror_config'], + result['pacman_config'], sort_keys=True, ) == json.dumps( - expected['mirror_config'], + expected['pacman_config'], sort_keys=True, ) From a56f0a6542c915cbe8a970e08e216935b409909a Mon Sep 17 00:00:00 2001 From: H4L0 Date: Tue, 12 May 2026 13:38:02 +0200 Subject: [PATCH 2/4] Move PacmanMenu to archinstall.lib.pacman package --- archinstall/lib/global_menu.py | 2 +- .../lib/{mirror/mirror_menu.py => pacman/pacman_menu.py} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename archinstall/lib/{mirror/mirror_menu.py => pacman/pacman_menu.py} (100%) diff --git a/archinstall/lib/global_menu.py b/archinstall/lib/global_menu.py index 292e6cda20..dbad6fa1ef 100644 --- a/archinstall/lib/global_menu.py +++ b/archinstall/lib/global_menu.py @@ -14,7 +14,6 @@ from archinstall.lib.locale.locale_menu import LocaleMenu from archinstall.lib.menu.abstract_menu import AbstractMenu, SpecialMenuKey from archinstall.lib.mirror.mirror_handler import MirrorListHandler -from archinstall.lib.mirror.mirror_menu import PacmanMenu from archinstall.lib.models.application import ApplicationConfiguration, ZramConfiguration from archinstall.lib.models.authentication import AuthenticationConfiguration from archinstall.lib.models.bootloader import Bootloader, BootloaderConfiguration @@ -29,6 +28,7 @@ from archinstall.lib.output import FormattedOutput from archinstall.lib.packages.packages import list_available_packages, select_additional_packages from archinstall.lib.pacman.config import PacmanConfig +from archinstall.lib.pacman.pacman_menu import PacmanMenu from archinstall.lib.translationhandler import Language, tr, translation_handler from archinstall.tui.components import tui from archinstall.tui.menu_item import MenuItem, MenuItemGroup diff --git a/archinstall/lib/mirror/mirror_menu.py b/archinstall/lib/pacman/pacman_menu.py similarity index 100% rename from archinstall/lib/mirror/mirror_menu.py rename to archinstall/lib/pacman/pacman_menu.py From f4800337a2c24df0756642b25ae9dafefbffb7a0 Mon Sep 17 00:00:00 2001 From: H4L0 Date: Tue, 12 May 2026 13:57:50 +0200 Subject: [PATCH 3/4] Move parallel_downloads into pacman_config in test fixture --- tests/data/test_config.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/data/test_config.json b/tests/data/test_config.json index a1bc4dbf8a..bdac0aebcf 100644 --- a/tests/data/test_config.json +++ b/tests/data/test_config.json @@ -159,7 +159,9 @@ "sign_check": "Required", "sign_option": "TrustAll" } - ] + ], + "parallel_downloads": 66, + "color": true }, "network_config": { "type": "manual", @@ -180,7 +182,6 @@ "packages": [ "firefox" ], - "parallel_downloads": 66, "profile_config": { "gfx_driver": "All open-source", "greeter": "lightdm-gtk-greeter", From 8d633dabac1d39a57e04f8ecc17f975474631e94 Mon Sep 17 00:00:00 2001 From: H4L0 Date: Tue, 12 May 2026 14:11:47 +0200 Subject: [PATCH 4/4] consistently rename lib.models.mirrors => lib.models.pacman --- archinstall/lib/args.py | 2 +- archinstall/lib/global_menu.py | 2 +- archinstall/lib/installer.py | 2 +- archinstall/lib/mirror/mirror_handler.py | 2 +- archinstall/lib/models/__init__.py | 2 +- archinstall/lib/models/{mirrors.py => pacman.py} | 0 archinstall/lib/pacman/config.py | 2 +- archinstall/lib/pacman/pacman_menu.py | 4 ++-- tests/test_args.py | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) rename archinstall/lib/models/{mirrors.py => pacman.py} (100%) diff --git a/archinstall/lib/args.py b/archinstall/lib/args.py index 1f56d6bdd3..eb58ce8f59 100644 --- a/archinstall/lib/args.py +++ b/archinstall/lib/args.py @@ -21,10 +21,10 @@ from archinstall.lib.models.config import SubConfig from archinstall.lib.models.device import DiskEncryption, DiskLayoutConfiguration from archinstall.lib.models.locale import LocaleConfiguration -from archinstall.lib.models.mirrors import PacmanConfiguration from archinstall.lib.models.network import NetworkConfiguration from archinstall.lib.models.package_types import DEFAULT_KERNEL from archinstall.lib.models.packages import Repository +from archinstall.lib.models.pacman import PacmanConfiguration from archinstall.lib.models.profile import ProfileConfiguration from archinstall.lib.models.users import Password, User, UserSerialization from archinstall.lib.output import debug, error, logger, warn diff --git a/archinstall/lib/global_menu.py b/archinstall/lib/global_menu.py index dbad6fa1ef..c065d7a9fd 100644 --- a/archinstall/lib/global_menu.py +++ b/archinstall/lib/global_menu.py @@ -19,10 +19,10 @@ from archinstall.lib.models.bootloader import Bootloader, BootloaderConfiguration from archinstall.lib.models.device import DiskLayoutConfiguration, DiskLayoutType, PartitionModification from archinstall.lib.models.locale import LocaleConfiguration -from archinstall.lib.models.mirrors import PacmanConfiguration from archinstall.lib.models.network import NetworkConfiguration, NicType from archinstall.lib.models.package_types import DEFAULT_KERNEL from archinstall.lib.models.packages import Repository +from archinstall.lib.models.pacman import PacmanConfiguration from archinstall.lib.models.profile import ProfileConfiguration from archinstall.lib.network.network_menu import select_network from archinstall.lib.output import FormattedOutput diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 309cffa138..2b940af0f4 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -46,10 +46,10 @@ Unit, ) from archinstall.lib.models.locale import LocaleConfiguration -from archinstall.lib.models.mirrors import PacmanConfiguration from archinstall.lib.models.network import Nic from archinstall.lib.models.package_types import DEFAULT_KERNEL, Kernel from archinstall.lib.models.packages import Repository +from archinstall.lib.models.pacman import PacmanConfiguration from archinstall.lib.models.users import User from archinstall.lib.output import debug, error, info, log, logger, warn from archinstall.lib.packages.packages import installed_package diff --git a/archinstall/lib/mirror/mirror_handler.py b/archinstall/lib/mirror/mirror_handler.py index ef0ba1a9e6..c1a2ea6861 100644 --- a/archinstall/lib/mirror/mirror_handler.py +++ b/archinstall/lib/mirror/mirror_handler.py @@ -3,7 +3,7 @@ from pathlib import Path from archinstall.lib.models import MirrorRegion -from archinstall.lib.models.mirrors import MirrorStatusEntryV3, MirrorStatusListV3 +from archinstall.lib.models.pacman import MirrorStatusEntryV3, MirrorStatusListV3 from archinstall.lib.networking import fetch_data_from_url from archinstall.lib.output import debug, info from archinstall.lib.pathnames import MIRRORLIST diff --git a/archinstall/lib/models/__init__.py b/archinstall/lib/models/__init__.py index 156f21ac1c..7e8e0f2dbf 100644 --- a/archinstall/lib/models/__init__.py +++ b/archinstall/lib/models/__init__.py @@ -27,9 +27,9 @@ _DeviceInfo, ) from archinstall.lib.models.locale import LocaleConfiguration -from archinstall.lib.models.mirrors import CustomRepository, MirrorRegion, PacmanConfiguration from archinstall.lib.models.network import NetworkConfiguration, Nic, NicType from archinstall.lib.models.packages import LocalPackage, PackageSearch, PackageSearchResult, Repository +from archinstall.lib.models.pacman import CustomRepository, MirrorRegion, PacmanConfiguration from archinstall.lib.models.profile import ProfileConfiguration from archinstall.lib.models.users import PasswordStrength, User diff --git a/archinstall/lib/models/mirrors.py b/archinstall/lib/models/pacman.py similarity index 100% rename from archinstall/lib/models/mirrors.py rename to archinstall/lib/models/pacman.py diff --git a/archinstall/lib/pacman/config.py b/archinstall/lib/pacman/config.py index f6619bf8ab..5785b31994 100644 --- a/archinstall/lib/pacman/config.py +++ b/archinstall/lib/pacman/config.py @@ -1,8 +1,8 @@ import re from pathlib import Path -from archinstall.lib.models.mirrors import PacmanConfiguration from archinstall.lib.models.packages import Repository +from archinstall.lib.models.pacman import PacmanConfiguration from archinstall.lib.pathnames import PACMAN_CONF diff --git a/archinstall/lib/pacman/pacman_menu.py b/archinstall/lib/pacman/pacman_menu.py index a6ac74816f..6939aa0210 100644 --- a/archinstall/lib/pacman/pacman_menu.py +++ b/archinstall/lib/pacman/pacman_menu.py @@ -4,7 +4,8 @@ from archinstall.lib.menu.helpers import Confirmation, Input, Loading, Selection from archinstall.lib.menu.list_manager import ListManager from archinstall.lib.mirror.mirror_handler import MirrorListHandler -from archinstall.lib.models.mirrors import ( +from archinstall.lib.models.packages import Repository +from archinstall.lib.models.pacman import ( CustomRepository, CustomServer, MirrorRegion, @@ -12,7 +13,6 @@ SignCheck, SignOption, ) -from archinstall.lib.models.packages import Repository from archinstall.lib.output import FormattedOutput from archinstall.lib.pathnames import PACMAN_CONF from archinstall.lib.translationhandler import tr diff --git a/tests/test_args.py b/tests/test_args.py index e6cea4378d..f8d4b4a2ea 100644 --- a/tests/test_args.py +++ b/tests/test_args.py @@ -19,9 +19,9 @@ from archinstall.lib.models.bootloader import Bootloader, BootloaderConfiguration from archinstall.lib.models.device import DiskLayoutConfiguration, DiskLayoutType from archinstall.lib.models.locale import LocaleConfiguration -from archinstall.lib.models.mirrors import CustomRepository, CustomServer, MirrorRegion, PacmanConfiguration, SignCheck, SignOption from archinstall.lib.models.network import NetworkConfiguration, Nic, NicType from archinstall.lib.models.packages import Repository +from archinstall.lib.models.pacman import CustomRepository, CustomServer, MirrorRegion, PacmanConfiguration, SignCheck, SignOption from archinstall.lib.models.profile import ProfileConfiguration from archinstall.lib.models.users import Password, User from archinstall.lib.profile.profiles_handler import profile_handler