Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion archinstall/lib/installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@
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)
Expand Down
2 changes: 1 addition & 1 deletion archinstall/lib/networking.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

from .exceptions import DownloadTimeout, SysCallError
from .output import debug, error, info
from .pacman import Pacman
from .pacman.pacman import Pacman


class DownloadTimer:
Expand Down
2 changes: 1 addition & 1 deletion archinstall/lib/packages/packages.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from ..exceptions import PackageError, SysCallError
from ..models.packages import AvailablePackage, LocalPackage, PackageSearch, PackageSearchResult, Repository
from ..output import debug
from ..pacman import Pacman
from ..pacman.pacman import Pacman

BASE_URL_PKG_SEARCH = 'https://archlinux.org/packages/search/json/'
# BASE_URL_PKG_CONTENT = 'https://archlinux.org/packages/search/json/'
Expand Down
90 changes: 0 additions & 90 deletions archinstall/lib/pacman/__init__.py
Original file line number Diff line number Diff line change
@@ -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',
]
83 changes: 83 additions & 0 deletions archinstall/lib/pacman/pacman.py
Original file line number Diff line number Diff line change
@@ -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,
)
2 changes: 1 addition & 1 deletion archinstall/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

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


Expand Down