From 5355c7141a503d1c6a96a9b15f500a85d4447909 Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Wed, 23 Jul 2025 16:52:01 +0200 Subject: [PATCH] Add --skip-boot to allow for bypassing installation of a bootloader (#3677) * Adding the option to skip boot loader * Fixed a variable complaint * Fixed ruff and mypy * Fixed mypy * Fixed mypy * Fixed import recursion * Fixed ruff * Fixed circular imports * Fixed ruff * Hiding the menu option for bootloader when --skip-boot is given. Still setting it default to None to avoid it sneaking into the config file or being set behind the scenes causing if statements to trigger. * Created an Enum None type for Bootloader called NO_BOOTLOADER * Fixed ruff * Spelling error * Hiding NO_BOOTLOADER if --skip-boot is omitted * Fixed TUI error when bootloader was missing * Fixed mypy complaints * Fixed ruff complaint * Made the Bootloader option visible during --skip-boot and set the default value to NO_BOOTLOADER when --skip-boot is present --- archinstall/lib/args.py | 13 +++++++++--- archinstall/lib/global_menu.py | 8 ++++++-- archinstall/lib/interactions/system_conf.py | 22 ++++++++++++++++----- archinstall/lib/models/bootloader.py | 13 +++++++++--- archinstall/scripts/guided.py | 7 ++++--- archinstall/tui/menu_item.py | 1 + tests/test_args.py | 3 +++ 7 files changed, 51 insertions(+), 16 deletions(-) diff --git a/archinstall/lib/args.py b/archinstall/lib/args.py index e5d7998c..eda61c32 100644 --- a/archinstall/lib/args.py +++ b/archinstall/lib/args.py @@ -43,6 +43,7 @@ class Arguments: mountpoint: Path = Path('/mnt') skip_ntp: bool = False skip_wkd: bool = False + skip_boot: bool = False debug: bool = False offline: bool = False no_pkg_lookups: bool = False @@ -62,7 +63,7 @@ class ArchConfig: profile_config: ProfileConfiguration | None = None mirror_config: MirrorConfiguration | None = None network_config: NetworkConfiguration | None = None - bootloader: Bootloader = field(default=Bootloader.get_default()) + bootloader: Bootloader | None = None uki: bool = False app_config: ApplicationConfiguration | None = None auth_config: AuthenticationConfiguration | None = None @@ -107,7 +108,7 @@ class ArchConfig: 'timezone': self.timezone, 'services': self.services, 'custom_commands': self.custom_commands, - 'bootloader': self.bootloader.json(), + 'bootloader': self.bootloader.json() if self.bootloader else None, 'app_config': self.app_config.json() if self.app_config else None, 'auth_config': self.auth_config.json() if self.auth_config else None, } @@ -179,7 +180,7 @@ class ArchConfig: if bootloader_config := args_config.get('bootloader', None): arch_config.bootloader = Bootloader.from_arg(bootloader_config) - if args_config.get('uki') and not arch_config.bootloader.has_uki_support(): + if args_config.get('uki') and (arch_config.bootloader is None or not arch_config.bootloader.has_uki_support()): arch_config.uki = False # deprecated: backwards compatibility @@ -367,6 +368,12 @@ class ArchConfigHandler: help='Disables checking if archlinux keyring wkd sync is complete.', default=False, ) + parser.add_argument( + '--skip-boot', + action='store_true', + help='Disables installation of a boot loader (note: only use this when problems arise with the boot loader step).', + default=False, + ) parser.add_argument( '--debug', action='store_true', diff --git a/archinstall/lib/global_menu.py b/archinstall/lib/global_menu.py index 05a79e4e..7995a27a 100644 --- a/archinstall/lib/global_menu.py +++ b/archinstall/lib/global_menu.py @@ -51,7 +51,7 @@ class GlobalMenu(AbstractMenu[None]): super().__init__(self._item_group, config=arch_config) def _get_menu_options(self) -> list[MenuItem]: - return [ + menu_options = [ MenuItem( text=tr('Archinstall language'), action=self._select_archinstall_language, @@ -189,6 +189,8 @@ class GlobalMenu(AbstractMenu[None]): ), ] + return menu_options + def _safe_config(self) -> None: # data: dict[str, Any] = {} # for item in self._item_group.items: @@ -416,11 +418,13 @@ class GlobalMenu(AbstractMenu[None]): XXX: The caller is responsible for wrapping the string with the translation shim if necessary. """ - bootloader = self._item_group.find_by_key('bootloader').value + bootloader: Bootloader | None = None root_partition: PartitionModification | None = None boot_partition: PartitionModification | None = None efi_partition: PartitionModification | None = None + bootloader = self._item_group.find_by_key('bootloader').value + if disk_config := self._item_group.find_by_key('disk_config').value: for layout in disk_config.device_modifications: if root_partition := layout.get_root_partition(): diff --git a/archinstall/lib/interactions/system_conf.py b/archinstall/lib/interactions/system_conf.py index 025612c6..99a1d8bd 100644 --- a/archinstall/lib/interactions/system_conf.py +++ b/archinstall/lib/interactions/system_conf.py @@ -6,6 +6,7 @@ from archinstall.tui.menu_item import MenuItem, MenuItemGroup from archinstall.tui.result import ResultType from archinstall.tui.types import Alignment, FrameProperties, FrameStyle, Orientation, PreviewStyle +from ..args import arch_config_handler from ..hardware import GfxDriver, SysInfo from ..models.bootloader import Bootloader @@ -47,14 +48,25 @@ def select_kernel(preset: list[str] = []) -> list[str]: def ask_for_bootloader(preset: Bootloader | None) -> Bootloader | None: # Systemd is UEFI only + options = [] + hidden_options = [] + default = None + header = None + + if arch_config_handler.args.skip_boot: + default = Bootloader.NO_BOOTLOADER + else: + hidden_options += [Bootloader.NO_BOOTLOADER] + if not SysInfo.has_uefi(): - options = [Bootloader.Grub, Bootloader.Limine] - default = Bootloader.Grub + options += [Bootloader.Grub, Bootloader.Limine] + if not default: + default = Bootloader.Grub header = tr('UEFI is not detected and some options are disabled') else: - options = [b for b in Bootloader] - default = Bootloader.Systemd - header = None + options += [b for b in Bootloader if b not in hidden_options] + if not default: + default = Bootloader.Systemd items = [MenuItem(o.value, value=o) for o in options] group = MenuItemGroup(items) diff --git a/archinstall/lib/models/bootloader.py b/archinstall/lib/models/bootloader.py index cea67d80..e1b7b839 100644 --- a/archinstall/lib/models/bootloader.py +++ b/archinstall/lib/models/bootloader.py @@ -8,6 +8,7 @@ from ..output import warn class Bootloader(Enum): + NO_BOOTLOADER = 'No bootloader' Systemd = 'Systemd-boot' Grub = 'Grub' Efistub = 'Efistub' @@ -25,11 +26,17 @@ class Bootloader(Enum): @staticmethod def values() -> list[str]: - return [e.value for e in Bootloader] + from ..args import arch_config_handler + + return [e.value for e in Bootloader if e != Bootloader.NO_BOOTLOADER or arch_config_handler.args.skip_boot is True] @classmethod - def get_default(cls) -> Bootloader: - if SysInfo.has_uefi(): + def get_default(cls) -> None | Bootloader: + from ..args import arch_config_handler + + if arch_config_handler.args.skip_boot: + return Bootloader.NO_BOOTLOADER + elif SysInfo.has_uefi(): return Bootloader.Systemd else: return Bootloader.Grub diff --git a/archinstall/scripts/guided.py b/archinstall/scripts/guided.py index bb16bad0..41763c78 100644 --- a/archinstall/scripts/guided.py +++ b/archinstall/scripts/guided.py @@ -99,10 +99,11 @@ def perform_installation(mountpoint: Path) -> None: if config.swap: installation.setup_swap('zram') - if config.bootloader == Bootloader.Grub and SysInfo.has_uefi(): - installation.add_additional_packages('grub') + if config.bootloader and config.bootloader != Bootloader.NO_BOOTLOADER: + if config.bootloader == Bootloader.Grub and SysInfo.has_uefi(): + installation.add_additional_packages('grub') - installation.add_bootloader(config.bootloader, config.uki) + installation.add_bootloader(config.bootloader, config.uki) # If user selected to copy the current ISO network configuration # Perform a copy of the config diff --git a/archinstall/tui/menu_item.py b/archinstall/tui/menu_item.py index 77d3cb09..0c9e74a1 100644 --- a/archinstall/tui/menu_item.py +++ b/archinstall/tui/menu_item.py @@ -196,6 +196,7 @@ class MenuItemGroup: max_width = self._max_items_text_width display_text = item.get_display_value() + default_text = self._default_suffix(item) text = unicode_ljust(str(item.text), max_width, ' ') diff --git a/tests/test_args.py b/tests/test_args.py index db98ebb3..895740e6 100644 --- a/tests/test_args.py +++ b/tests/test_args.py @@ -36,6 +36,7 @@ def test_default_args(monkeypatch: MonkeyPatch) -> None: mountpoint=Path('/mnt'), skip_ntp=False, skip_wkd=False, + skip_boot=False, debug=False, offline=False, no_pkg_lookups=False, @@ -66,6 +67,7 @@ def test_correct_parsing_args( '/tmp', '--skip-ntp', '--skip-wkd', + '--skip-boot', '--debug', '--offline', '--no-pkg-lookups', @@ -91,6 +93,7 @@ def test_correct_parsing_args( mountpoint=Path('/tmp'), skip_ntp=True, skip_wkd=True, + skip_boot=True, debug=True, offline=True, no_pkg_lookups=True,