From cf478a0b73bcb3b95bfee3fa90c3a66c15037a4b Mon Sep 17 00:00:00 2001 From: Softer Date: Sat, 6 Jun 2026 14:00:27 +0300 Subject: [PATCH 1/3] Skip custom mirror config when keyring sync fails Check the exit state of archlinux-keyring-wkd-sync.service after waiting for it to finish. If it failed, skip set_mirrors() and continue installation with default mirrors instead of crashing with "GPGME error: No data" during pacman -Syy. --- archinstall/lib/installer.py | 9 +++++++++ archinstall/scripts/guided.py | 12 +++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index bf7d91ba31..40049c5393 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -131,6 +131,7 @@ def __init__( self._zram_enabled = False self._disable_fstrim = False + self._keyring_synced = True self.pacman = Pacman(self.target, silent) @@ -228,6 +229,10 @@ def _verify_service_stop(self, offline: bool, skip_ntp: bool, skip_wkd: bool) -> while self._service_state('archlinux-keyring-wkd-sync.service') not in ('dead', 'failed', 'exited'): time.sleep(1) + if self._service_state('archlinux-keyring-wkd-sync.service') == 'failed': + self._keyring_synced = False + warn('archlinux-keyring-wkd-sync failed, custom mirror configuration will be skipped') + def _verify_boot_part(self) -> None: """ Check that mounted /boot device has at minimum size for installation @@ -246,6 +251,10 @@ def _verify_boot_part(self) -> None: f'Please resize it to at least 200MiB and re-run the installation.', ) + @property + def keyring_synced(self) -> bool: + return self._keyring_synced + def sanity_check( self, offline: bool = False, diff --git a/archinstall/scripts/guided.py b/archinstall/scripts/guided.py index 6f484910de..1355d18f32 100644 --- a/archinstall/scripts/guided.py +++ b/archinstall/scripts/guided.py @@ -12,7 +12,7 @@ from archinstall.lib.general.general_menu import PostInstallationAction, select_post_installation from archinstall.lib.global_menu import GlobalMenu from archinstall.lib.installer import Installer, accessibility_tools_in_use, run_custom_user_commands -from archinstall.lib.log import debug, error, info +from archinstall.lib.log import debug, error, info, warn from archinstall.lib.menu.util import delayed_warning from archinstall.lib.mirror.mirror_handler import MirrorListHandler from archinstall.lib.models import Bootloader @@ -98,7 +98,10 @@ def perform_installation( installation.generate_key_files() if mirror_config := config.mirror_config: - installation.set_mirrors(mirror_list_handler, mirror_config, on_target=False) + if installation.keyring_synced: + installation.set_mirrors(mirror_list_handler, mirror_config, on_target=False) + else: + warn('Skipping custom mirror configuration due to keyring sync failure') installation.minimal_installation( optional_repositories=optional_repositories, @@ -109,7 +112,10 @@ def perform_installation( ) if mirror_config := config.mirror_config: - installation.set_mirrors(mirror_list_handler, mirror_config, on_target=True) + if installation.keyring_synced: + installation.set_mirrors(mirror_list_handler, mirror_config, on_target=True) + else: + warn('Skipping target mirror configuration due to keyring sync failure') if config.swap and config.swap.enabled: installation.setup_swap(algo=config.swap.algorithm) From 490e71f3ae891ba16e46146b48591d10a0ae21ba Mon Sep 17 00:00:00 2001 From: Softer Date: Sat, 6 Jun 2026 16:42:02 +0300 Subject: [PATCH 2/3] Reinit keyring automatically when pacman sync fails with GPGME error Instead of skipping mirror configuration when wkd-sync fails, catch the GPGME error at the point where it actually occurs - during pacman -Syy. If the sync fails with a keyring-related error, reinit the keyring with pacman-key --init/--populate and retry the sync. --- archinstall/lib/installer.py | 8 +----- archinstall/lib/pacman/pacman.py | 48 ++++++++++++++++++++++++++------ archinstall/scripts/guided.py | 12 ++------ 3 files changed, 43 insertions(+), 25 deletions(-) diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 40049c5393..d577215ce8 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -131,7 +131,6 @@ def __init__( self._zram_enabled = False self._disable_fstrim = False - self._keyring_synced = True self.pacman = Pacman(self.target, silent) @@ -230,8 +229,7 @@ def _verify_service_stop(self, offline: bool, skip_ntp: bool, skip_wkd: bool) -> time.sleep(1) if self._service_state('archlinux-keyring-wkd-sync.service') == 'failed': - self._keyring_synced = False - warn('archlinux-keyring-wkd-sync failed, custom mirror configuration will be skipped') + warn('archlinux-keyring-wkd-sync failed, keyring may need reinit during pacman sync') def _verify_boot_part(self) -> None: """ @@ -251,10 +249,6 @@ def _verify_boot_part(self) -> None: f'Please resize it to at least 200MiB and re-run the installation.', ) - @property - def keyring_synced(self) -> bool: - return self._keyring_synced - def sanity_check( self, offline: bool = False, diff --git a/archinstall/lib/pacman/pacman.py b/archinstall/lib/pacman/pacman.py index c78f6145d2..bd8454109c 100644 --- a/archinstall/lib/pacman/pacman.py +++ b/archinstall/lib/pacman/pacman.py @@ -4,8 +4,8 @@ from pathlib import Path from archinstall.lib.command import SysCommand -from archinstall.lib.exceptions import RequirementError -from archinstall.lib.log import error, info, warn +from archinstall.lib.exceptions import RequirementError, SysCallError +from archinstall.lib.log import debug, error, info, warn from archinstall.lib.pathnames import PACMAN_CONF from archinstall.lib.plugins import plugins from archinstall.lib.translationhandler import tr @@ -53,15 +53,45 @@ def ask(self, error_message: str, bail_message: str, func: Callable, *args, **kw 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', - ) + + try: + self.run('-Syy', default_cmd='pacman') + except SysCallError as err: + if b'GPGME' in err.worker_log or b'keyring' in err.worker_log.lower(): + warn('Pacman sync failed with keyring error, attempting keyring reinit') + self._reinit_keyring() + self.ask( + 'Could not sync a new package database after keyring reinit', + 'Could not sync mirrors', + self.run, + '-Syy', + default_cmd='pacman', + ) + else: + self.ask( + 'Could not sync a new package database', + 'Could not sync mirrors', + self.run, + '-Syy', + default_cmd='pacman', + ) + self.synced = True + @staticmethod + def _reinit_keyring() -> None: + try: + SysCommand('killall gpg-agent') + except SysCallError as err: + debug(f'killall gpg-agent failed (may not be running): {err}') + + try: + SysCommand('pacman-key --init') + SysCommand('pacman-key --populate archlinux') + debug('Keyring reinitialized successfully') + except SysCallError as err: + debug(f'Keyring reinit failed: {err}') + def strap(self, packages: str | list[str]) -> None: self.sync() if isinstance(packages, str): diff --git a/archinstall/scripts/guided.py b/archinstall/scripts/guided.py index 1355d18f32..6f484910de 100644 --- a/archinstall/scripts/guided.py +++ b/archinstall/scripts/guided.py @@ -12,7 +12,7 @@ from archinstall.lib.general.general_menu import PostInstallationAction, select_post_installation from archinstall.lib.global_menu import GlobalMenu from archinstall.lib.installer import Installer, accessibility_tools_in_use, run_custom_user_commands -from archinstall.lib.log import debug, error, info, warn +from archinstall.lib.log import debug, error, info from archinstall.lib.menu.util import delayed_warning from archinstall.lib.mirror.mirror_handler import MirrorListHandler from archinstall.lib.models import Bootloader @@ -98,10 +98,7 @@ def perform_installation( installation.generate_key_files() if mirror_config := config.mirror_config: - if installation.keyring_synced: - installation.set_mirrors(mirror_list_handler, mirror_config, on_target=False) - else: - warn('Skipping custom mirror configuration due to keyring sync failure') + installation.set_mirrors(mirror_list_handler, mirror_config, on_target=False) installation.minimal_installation( optional_repositories=optional_repositories, @@ -112,10 +109,7 @@ def perform_installation( ) if mirror_config := config.mirror_config: - if installation.keyring_synced: - installation.set_mirrors(mirror_list_handler, mirror_config, on_target=True) - else: - warn('Skipping target mirror configuration due to keyring sync failure') + installation.set_mirrors(mirror_list_handler, mirror_config, on_target=True) if config.swap and config.swap.enabled: installation.setup_swap(algo=config.swap.algorithm) From 6b9cc77633d940859f3ee8f08f4042df285a9b11 Mon Sep 17 00:00:00 2001 From: Softer Date: Mon, 8 Jun 2026 02:28:39 +0300 Subject: [PATCH 3/3] Simplify pacman sync() and guard gpg-agent kill on running state Collapse the duplicated self.ask() blocks in sync() into a single call via a msg variable, and drop the redundant default_cmd='pacman' (it is already the default). In _reinit_keyring(), check that gpg-agent is actually running (pgrep -x) before killing it, instead of catching the killall error and assuming it was not running. --- archinstall/lib/pacman/pacman.py | 38 ++++++++++++++++---------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/archinstall/lib/pacman/pacman.py b/archinstall/lib/pacman/pacman.py index bd8454109c..83b965e2e0 100644 --- a/archinstall/lib/pacman/pacman.py +++ b/archinstall/lib/pacman/pacman.py @@ -55,35 +55,35 @@ def sync(self) -> None: return try: - self.run('-Syy', default_cmd='pacman') + self.run('-Syy') except SysCallError as err: if b'GPGME' in err.worker_log or b'keyring' in err.worker_log.lower(): warn('Pacman sync failed with keyring error, attempting keyring reinit') self._reinit_keyring() - self.ask( - 'Could not sync a new package database after keyring reinit', - 'Could not sync mirrors', - self.run, - '-Syy', - default_cmd='pacman', - ) + msg = 'Could not sync a new package database after keyring reinit' else: - self.ask( - 'Could not sync a new package database', - 'Could not sync mirrors', - self.run, - '-Syy', - default_cmd='pacman', - ) + msg = 'Could not sync a new package database' + + self.ask(msg, 'Could not sync mirrors', self.run, '-Syy') self.synced = True @staticmethod - def _reinit_keyring() -> None: + def _is_running(process: str) -> bool: try: - SysCommand('killall gpg-agent') - except SysCallError as err: - debug(f'killall gpg-agent failed (may not be running): {err}') + SysCommand(f'pgrep -x {process}') + return True + except SysCallError: + debug(f'{process} is not running') + return False + + @staticmethod + def _reinit_keyring() -> None: + if Pacman._is_running('gpg-agent'): + try: + SysCommand('killall gpg-agent') + except SysCallError as err: + debug(f'Failed to kill gpg-agent: {err}') try: SysCommand('pacman-key --init')