|
1 | | -import sys |
2 | | -import time |
3 | | -from collections.abc import Callable |
4 | | -from pathlib import Path |
5 | | - |
6 | | -from archinstall.lib.translationhandler import tr |
7 | | - |
8 | | -from ..exceptions import RequirementError |
9 | | -from ..general import SysCommand |
10 | | -from ..output import error, info, warn |
11 | | -from ..plugins import plugins |
12 | | -from .config import PacmanConfig |
13 | | - |
14 | | - |
15 | | -class Pacman: |
16 | | - def __init__(self, target: Path, silent: bool = False): |
17 | | - self.synced = False |
18 | | - self.silent = silent |
19 | | - self.target = target |
20 | | - |
21 | | - @staticmethod |
22 | | - def run(args: str, default_cmd: str = 'pacman') -> SysCommand: |
23 | | - """ |
24 | | - A centralized function to call `pacman` from. |
25 | | - It also protects us from colliding with other running pacman sessions (if used locally). |
26 | | - The grace period is set to 10 minutes before exiting hard if another pacman instance is running. |
27 | | - """ |
28 | | - pacman_db_lock = Path('/var/lib/pacman/db.lck') |
29 | | - |
30 | | - if pacman_db_lock.exists(): |
31 | | - warn(tr('Pacman is already running, waiting maximum 10 minutes for it to terminate.')) |
32 | | - |
33 | | - started = time.time() |
34 | | - while pacman_db_lock.exists(): |
35 | | - time.sleep(0.25) |
36 | | - |
37 | | - if time.time() - started > (60 * 10): |
38 | | - error(tr('Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall.')) |
39 | | - sys.exit(1) |
40 | | - |
41 | | - return SysCommand(f'{default_cmd} {args}') |
42 | | - |
43 | | - def ask(self, error_message: str, bail_message: str, func: Callable, *args, **kwargs) -> None: # type: ignore[no-untyped-def, type-arg] |
44 | | - while True: |
45 | | - try: |
46 | | - func(*args, **kwargs) |
47 | | - break |
48 | | - except Exception as err: |
49 | | - error(f'{error_message}: {err}') |
50 | | - if not self.silent and input('Would you like to re-try this download? (Y/n): ').lower().strip() in 'y': |
51 | | - continue |
52 | | - raise RequirementError(f'{bail_message}: {err}') |
53 | | - |
54 | | - def sync(self) -> None: |
55 | | - if self.synced: |
56 | | - return |
57 | | - self.ask( |
58 | | - 'Could not sync a new package database', |
59 | | - 'Could not sync mirrors', |
60 | | - self.run, |
61 | | - '-Syy', |
62 | | - default_cmd='pacman', |
63 | | - ) |
64 | | - self.synced = True |
65 | | - |
66 | | - def strap(self, packages: str | list[str]) -> None: |
67 | | - self.sync() |
68 | | - if isinstance(packages, str): |
69 | | - packages = [packages] |
70 | | - |
71 | | - for plugin in plugins.values(): |
72 | | - if hasattr(plugin, 'on_pacstrap'): |
73 | | - if result := plugin.on_pacstrap(packages): |
74 | | - packages = result |
75 | | - |
76 | | - info(f'Installing packages: {packages}') |
77 | | - |
78 | | - self.ask( |
79 | | - 'Could not strap in packages', |
80 | | - 'Pacstrap failed. See /var/log/archinstall/install.log or above message for error details', |
81 | | - SysCommand, |
82 | | - f'pacstrap -C /etc/pacman.conf -K {self.target} {" ".join(packages)} --noconfirm --needed', |
83 | | - peek_output=True, |
84 | | - ) |
85 | | - |
86 | | - |
87 | | -__all__ = [ |
88 | | - 'Pacman', |
89 | | - 'PacmanConfig', |
90 | | -] |
0 commit comments