From 2421e538a8e35dedd527be569c1872d82bf4ce5f Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Tue, 27 Jan 2026 12:34:48 +1100 Subject: [PATCH] Move Pacman out of init (#4150) * Move pacman out of init * Update archinstall/lib/pacman/pacman.py Co-authored-by: codefiles <11915375+codefiles@users.noreply.github.com> * Update archinstall/lib/pacman/pacman.py Co-authored-by: codefiles <11915375+codefiles@users.noreply.github.com> --------- Co-authored-by: codefiles <11915375+codefiles@users.noreply.github.com> --- archinstall/lib/installer.py | 2 +- archinstall/lib/networking.py | 2 +- archinstall/lib/packages/packages.py | 2 +- archinstall/lib/pacman/__init__.py | 90 ---------------------------- archinstall/lib/pacman/pacman.py | 83 +++++++++++++++++++++++++ archinstall/main.py | 2 +- 6 files changed, 87 insertions(+), 94 deletions(-) create mode 100644 archinstall/lib/pacman/pacman.py diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 90d8b9fb..c2947261 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -49,8 +49,8 @@ from .models.mirrors import MirrorConfiguration from .models.network import Nic from .models.users import User from .output import debug, error, info, log, logger, warn -from .pacman import Pacman from .pacman.config import PacmanConfig +from .pacman.pacman import Pacman from .plugins import plugins # Any package that the Installer() is responsible for (optional and the default ones) diff --git a/archinstall/lib/networking.py b/archinstall/lib/networking.py index c06c0346..58e808f9 100644 --- a/archinstall/lib/networking.py +++ b/archinstall/lib/networking.py @@ -14,7 +14,7 @@ from urllib.request import urlopen from .exceptions import DownloadTimeout, SysCallError from .output import debug, error, info -from .pacman import Pacman +from .pacman.pacman import Pacman class DownloadTimer: diff --git a/archinstall/lib/packages/packages.py b/archinstall/lib/packages/packages.py index 83a376c0..d95ec6ed 100644 --- a/archinstall/lib/packages/packages.py +++ b/archinstall/lib/packages/packages.py @@ -3,7 +3,7 @@ from functools import lru_cache from ..exceptions import SysCallError from ..models.packages import AvailablePackage, LocalPackage, Repository from ..output import debug -from ..pacman import Pacman +from ..pacman.pacman import Pacman # TODO: This shouldn't be living in here but there are too many diff --git a/archinstall/lib/pacman/__init__.py b/archinstall/lib/pacman/__init__.py index 7fe5f73e..e69de29b 100644 --- a/archinstall/lib/pacman/__init__.py +++ b/archinstall/lib/pacman/__init__.py @@ -1,90 +0,0 @@ -import sys -import time -from collections.abc import Callable -from pathlib import Path - -from archinstall.lib.translationhandler import tr - -from ..exceptions import RequirementError -from ..general import SysCommand -from ..output import error, info, warn -from ..plugins import plugins -from .config import PacmanConfig - - -class Pacman: - def __init__(self, target: Path, silent: bool = False): - self.synced = False - self.silent = silent - self.target = target - - @staticmethod - def run(args: str, default_cmd: str = 'pacman') -> SysCommand: - """ - A centralized function to call `pacman` from. - It also protects us from colliding with other running pacman sessions (if used locally). - The grace period is set to 10 minutes before exiting hard if another pacman instance is running. - """ - pacman_db_lock = Path('/var/lib/pacman/db.lck') - - if pacman_db_lock.exists(): - warn(tr('Pacman is already running, waiting maximum 10 minutes for it to terminate.')) - - started = time.time() - while pacman_db_lock.exists(): - time.sleep(0.25) - - if time.time() - started > (60 * 10): - error(tr('Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall.')) - sys.exit(1) - - return SysCommand(f'{default_cmd} {args}') - - def ask(self, error_message: str, bail_message: str, func: Callable, *args, **kwargs) -> None: # type: ignore[no-untyped-def, type-arg] - while True: - try: - func(*args, **kwargs) - break - except Exception as err: - error(f'{error_message}: {err}') - if not self.silent and input('Would you like to re-try this download? (Y/n): ').lower().strip() in 'y': - continue - raise RequirementError(f'{bail_message}: {err}') - - def sync(self) -> None: - if self.synced: - return - self.ask( - 'Could not sync a new package database', - 'Could not sync mirrors', - self.run, - '-Syy', - default_cmd='pacman', - ) - self.synced = True - - def strap(self, packages: str | list[str]) -> None: - self.sync() - if isinstance(packages, str): - packages = [packages] - - for plugin in plugins.values(): - if hasattr(plugin, 'on_pacstrap'): - if result := plugin.on_pacstrap(packages): - packages = result - - info(f'Installing packages: {packages}') - - self.ask( - 'Could not strap in packages', - 'Pacstrap failed. See /var/log/archinstall/install.log or above message for error details', - SysCommand, - f'pacstrap -C /etc/pacman.conf -K {self.target} {" ".join(packages)} --noconfirm --needed', - peek_output=True, - ) - - -__all__ = [ - 'Pacman', - 'PacmanConfig', -] diff --git a/archinstall/lib/pacman/pacman.py b/archinstall/lib/pacman/pacman.py new file mode 100644 index 00000000..f3a22472 --- /dev/null +++ b/archinstall/lib/pacman/pacman.py @@ -0,0 +1,83 @@ +import sys +import time +from collections.abc import Callable +from pathlib import Path + +from archinstall.lib.translationhandler import tr + +from ..exceptions import RequirementError +from ..general import SysCommand +from ..output import error, info, warn +from ..plugins import plugins + + +class Pacman: + def __init__(self, target: Path, silent: bool = False): + self.synced = False + self.silent = silent + self.target = target + + @staticmethod + def run(args: str, default_cmd: str = 'pacman') -> SysCommand: + """ + A centralized function to call `pacman` from. + It also protects us from colliding with other running pacman sessions (if used locally). + The grace period is set to 10 minutes before exiting hard if another pacman instance is running. + """ + pacman_db_lock = Path('/var/lib/pacman/db.lck') + + if pacman_db_lock.exists(): + warn(tr('Pacman is already running, waiting maximum 10 minutes for it to terminate.')) + + started = time.time() + while pacman_db_lock.exists(): + time.sleep(0.25) + + if time.time() - started > (60 * 10): + error(tr('Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall.')) + sys.exit(1) + + return SysCommand(f'{default_cmd} {args}') + + def ask(self, error_message: str, bail_message: str, func: Callable, *args, **kwargs) -> None: # type: ignore[no-untyped-def, type-arg] + while True: + try: + func(*args, **kwargs) + break + except Exception as err: + error(f'{error_message}: {err}') + if not self.silent and input('Would you like to re-try this download? (Y/n): ').lower().strip() in 'y': + continue + raise RequirementError(f'{bail_message}: {err}') + + def sync(self) -> None: + if self.synced: + return + self.ask( + 'Could not sync a new package database', + 'Could not sync mirrors', + self.run, + '-Syy', + default_cmd='pacman', + ) + self.synced = True + + def strap(self, packages: str | list[str]) -> None: + self.sync() + if isinstance(packages, str): + packages = [packages] + + for plugin in plugins.values(): + if hasattr(plugin, 'on_pacstrap'): + if result := plugin.on_pacstrap(packages): + packages = result + + info(f'Installing packages: {packages}') + + self.ask( + 'Could not strap in packages', + 'Pacstrap failed. See /var/log/archinstall/install.log or above message for error details', + SysCommand, + f'pacstrap -C /etc/pacman.conf -K {self.target} {" ".join(packages)} --noconfirm --needed', + peek_output=True, + ) diff --git a/archinstall/main.py b/archinstall/main.py index 712557a1..e0b045ac 100644 --- a/archinstall/main.py +++ b/archinstall/main.py @@ -16,7 +16,7 @@ from archinstall.lib.packages.packages import check_version_upgrade from .lib.hardware import SysInfo from .lib.output import debug, error, info, warn -from .lib.pacman import Pacman +from .lib.pacman.pacman import Pacman from .lib.translationhandler import tr