Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions archinstall/lib/args.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from archinstall.lib.models.mirrors import MirrorConfiguration
from archinstall.lib.models.network import NetworkConfiguration
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
Expand Down Expand Up @@ -73,7 +74,7 @@ class ArchConfig:
kernels: list[str] = field(default_factory=lambda: ['linux'])
ntp: bool = True
packages: list[str] = field(default_factory=list)
parallel_downloads: int = 0
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)
Expand Down Expand Up @@ -104,7 +105,7 @@ def safe_config(self) -> dict[str, Any]:
'kernels': self.kernels,
'ntp': self.ntp,
'packages': self.packages,
'parallel_downloads': self.parallel_downloads,
'pacman_config': self.pacman_config.json(),
'swap': self.swap,
'timezone': self.timezone,
'services': self.services,
Expand Down Expand Up @@ -209,8 +210,10 @@ def from_config(cls, args_config: dict[str, Any], args: Arguments) -> Self:
if packages := args_config.get('packages', []):
arch_config.packages = packages

if parallel_downloads := args_config.get('parallel_downloads', 0):
arch_config.parallel_downloads = parallel_downloads
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:
Expand Down
2 changes: 0 additions & 2 deletions archinstall/lib/general/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from archinstall.lib.general.general_menu import (
add_number_of_parallel_downloads,
select_archinstall_language,
select_hostname,
select_ntp,
Expand All @@ -8,7 +7,6 @@
from archinstall.lib.general.system_menu import select_driver, select_kernel, select_swap

__all__ = [
'add_number_of_parallel_downloads',
'select_archinstall_language',
'select_driver',
'select_hostname',
Expand Down
50 changes: 0 additions & 50 deletions archinstall/lib/general/general_menu.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from archinstall.lib.locale.utils import list_timezones
from archinstall.lib.menu.helpers import Confirmation, Input, Selection
from archinstall.lib.output import warn
from archinstall.lib.pathnames import PACMAN_CONF
from archinstall.lib.translationhandler import Language, tr
from archinstall.tui.ui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.ui.result import ResultType
Expand Down Expand Up @@ -126,55 +125,6 @@ async def select_archinstall_language(languages: list[Language], preset: Languag
raise ValueError('Language selection not handled')


async def add_number_of_parallel_downloads(preset: int = 1) -> int | None:
max_recommended = 5

header = tr('This option enables the number of parallel downloads that can occur during package downloads') + '\n'
header += tr(' - Maximum recommended value : {} ( Allows {} parallel downloads at a time )').format(max_recommended, max_recommended) + '\n\n'
header += tr('Enter the number of parallel downloads to be enabled')

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

downloads = 1

match result.type_:
case ResultType.Skip:
return preset
case ResultType.Reset:
return downloads
case ResultType.Selection:
downloads = int(result.get_value())

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 = {downloads}\n')
else:
fwrite.write(f'{line}\n')

return downloads


async def select_post_installation(elapsed_time: float | None = None) -> PostInstallationAction:
header = 'Installation completed'
if elapsed_time is not None:
Expand Down
26 changes: 16 additions & 10 deletions archinstall/lib/global_menu.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from archinstall.lib.bootloader.bootloader_menu import BootloaderMenu
from archinstall.lib.configuration import save_config
from archinstall.lib.disk.disk_menu import DiskLayoutConfigurationMenu
from archinstall.lib.general.general_menu import add_number_of_parallel_downloads, select_hostname, select_ntp, select_timezone
from archinstall.lib.general.general_menu import select_hostname, select_ntp, select_timezone
from archinstall.lib.general.system_menu import select_kernel, select_swap
from archinstall.lib.hardware import SysInfo
from archinstall.lib.locale.locale_menu import LocaleMenu
Expand All @@ -22,11 +22,13 @@
from archinstall.lib.models.mirrors import MirrorConfiguration
from archinstall.lib.models.network import NetworkConfiguration, 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.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.ui.menu_item import MenuItem, MenuItemGroup

Expand Down Expand Up @@ -137,11 +139,11 @@ def _get_menu_options(self) -> list[MenuItem]:
key='network_config',
),
MenuItem(
text=tr('Parallel Downloads'),
action=add_number_of_parallel_downloads,
value=1,
preview_action=self._prev_parallel_dw,
key='parallel_downloads',
text=tr('Pacman'),
action=self._pacman_configuration,
value=PacmanConfiguration.default(),
preview_action=self._prev_pacman_config,
key='pacman_config',
),
MenuItem(
text=tr('Additional packages'),
Expand Down Expand Up @@ -410,10 +412,14 @@ def _prev_hostname(self, item: MenuItem) -> str | None:
return f'{tr("Hostname")}: {item.value}'
return None

def _prev_parallel_dw(self, item: MenuItem) -> str | None:
if item.value is not None:
return f'{tr("Parallel Downloads")}: {item.value}'
return None
async def _pacman_configuration(self, preset: PacmanConfiguration) -> PacmanConfiguration | None:
return await PacmanMenu(preset).show()

def _prev_pacman_config(self, item: MenuItem) -> str | None:
if not item.value:
return None
config: PacmanConfiguration = item.value
return config.preview()

def _prev_kernel(self, item: MenuItem) -> str | None:
if item.value:
Expand Down
5 changes: 5 additions & 0 deletions archinstall/lib/installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
from archinstall.lib.models.mirrors import MirrorConfiguration
from archinstall.lib.models.network import Nic
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
Expand Down Expand Up @@ -883,6 +884,7 @@ def minimal_installation(
mkinitcpio: bool = True,
hostname: str | None = None,
locale_config: LocaleConfiguration | None = LocaleConfiguration.default(),
pacman_config: PacmanConfiguration | None = None,
) -> None:
if self._disk_config.lvm_config:
lvm = 'lvm2'
Expand Down Expand Up @@ -929,6 +931,9 @@ def minimal_installation(

pacman_conf.persist()

if pacman_config:
pacman_conf.configure(pacman_config)

# Periodic TRIM may improve the performance and longevity of SSDs whilst
# having no adverse effect on other devices. Most distributions enable
# periodic TRIM by default.
Expand Down
37 changes: 37 additions & 0 deletions archinstall/lib/models/pacman.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from dataclasses import dataclass
from typing import Any, Self

from archinstall.lib.translationhandler import tr


@dataclass
class PacmanConfiguration:
parallel_downloads: int = 5
color: bool = True

@classmethod
def default(cls) -> Self:
return cls()

def json(self) -> dict[str, Any]:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return {
'parallel_downloads': self.parallel_downloads,
'color': self.color,
}

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: dict[str, Any]) -> 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
21 changes: 20 additions & 1 deletion archinstall/lib/pacman/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from pathlib import Path

from archinstall.lib.models.packages import Repository
from archinstall.lib.models.pacman import PacmanConfiguration
from archinstall.lib.pathnames import PACMAN_CONF


Expand Down Expand Up @@ -50,5 +51,23 @@ def apply(self) -> None:
f.writelines(content)

def persist(self) -> None:
if self._repositories and self._config_remote_path:
if self._config_remote_path:
PACMAN_CONF.copy(self._config_remote_path, preserve_metadata=True)

def configure(self, pacman_config: PacmanConfiguration) -> None:
"""Apply PacmanConfiguration (Color, ParallelDownloads) to the target system's pacman.conf."""
if not self._config_remote_path or not self._config_remote_path.exists():
return

content = self._config_remote_path.read_text().splitlines()
result = []

for line in content:
if re.match(r'^#?\s*ParallelDownloads', line):
result.append(f'ParallelDownloads = {pacman_config.parallel_downloads}')
elif re.match(r'^#?\s*Color\s*$', line):
result.append('Color' if pacman_config.color else '#Color')
else:
result.append(line)

self._config_remote_path.write_text('\n'.join(result) + '\n')
114 changes: 114 additions & 0 deletions archinstall/lib/pacman/pacman_menu.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
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.ui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.ui.result import ResultType


class PacmanMenu(AbstractSubMenu[PacmanConfiguration]):
def __init__(
self,
pacman_conf: PacmanConfiguration,
):
self._pacman_conf = pacman_conf
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',
),
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()
Loading
Loading