From f60f9ff0322ea90d9ccf47ae6e1b2a1f4f28bb88 Mon Sep 17 00:00:00 2001 From: h8d13 Date: Wed, 12 Aug 2026 14:03:32 +0200 Subject: [PATCH 1/5] feat(#4466): support `optdepends` of `linux-firmware` Signed-off-by: h8d13 --- archinstall/lib/args.py | 6 ++++++ archinstall/lib/general/system_menu.py | 28 ++++++++++++++++++++++++- archinstall/lib/global_menu.py | 15 ++++++++++++- archinstall/lib/installer.py | 5 +++++ archinstall/lib/models/package_types.py | 17 +++++++++++++++ archinstall/scripts/guided.py | 1 + 6 files changed, 70 insertions(+), 2 deletions(-) diff --git a/archinstall/lib/args.py b/archinstall/lib/args.py index b2003d4bec..602fcbd6e1 100644 --- a/archinstall/lib/args.py +++ b/archinstall/lib/args.py @@ -88,6 +88,7 @@ class ArchConfigType(StrEnum): ENCRYPTION_PASSWORD = 'encryption_password' HOSTNAME = 'hostname' KERNELS = 'kernels' + FIRMWARE_OPTDEPS = 'firmware_optdeps' NTP = 'ntp' TIMEZONE = 'timezone' SERVICES = 'services' @@ -166,6 +167,7 @@ class ArchConfig: swap: ZramConfiguration | None = None hostname: str = 'archlinux' kernels: list[str] = field(default_factory=lambda: [DEFAULT_KERNEL.value]) + firmware_optdeps: list[str] = field(default_factory=list) ntp: bool = True packages: list[str] = field(default_factory=list) pacman_config: PacmanConfiguration = field(default_factory=PacmanConfiguration) @@ -211,6 +213,7 @@ def plain_cfg(self) -> dict[ArchConfigType, str | list[str] | bool]: return { ArchConfigType.HOSTNAME: self.hostname, ArchConfigType.KERNELS: self.kernels, + ArchConfigType.FIRMWARE_OPTDEPS: self.firmware_optdeps, ArchConfigType.NTP: self.ntp, ArchConfigType.TIMEZONE: self.timezone, ArchConfigType.SERVICES: self.services, @@ -326,6 +329,9 @@ def from_config(cls, args_config: dict[str, Any], args: Arguments) -> Self: if kernels := args_config.get('kernels', []): arch_config.kernels = kernels + if firmware_optdeps := args_config.get('firmware_optdeps', []): + arch_config.firmware_optdeps = firmware_optdeps + arch_config.ntp = args_config.get('ntp', True) if packages := args_config.get('packages', []): diff --git a/archinstall/lib/general/system_menu.py b/archinstall/lib/general/system_menu.py index 82dc4cdb70..4f26c4a57d 100644 --- a/archinstall/lib/general/system_menu.py +++ b/archinstall/lib/general/system_menu.py @@ -3,7 +3,7 @@ from archinstall.lib.hardware import GfxDriver, SysInfo from archinstall.lib.menu.helpers import Confirmation, Selection from archinstall.lib.models.application import ZramAlgorithm, ZramConfiguration -from archinstall.lib.models.package_types import DEFAULT_KERNEL, Kernel +from archinstall.lib.models.package_types import DEFAULT_KERNEL, FirmwareOptdep, Kernel from archinstall.lib.translationhandler import tr from archinstall.tui.menu_item import MenuItem, MenuItemGroup from archinstall.tui.result import ResultType @@ -37,6 +37,32 @@ async def select_kernel(preset: list[Kernel] = []) -> list[Kernel]: return result.get_values() +async def select_firmware_optdeps(preset: list[FirmwareOptdep] = []) -> list[FirmwareOptdep]: + """ + Asks the user which of linux-firmware's optional dependencies to install. + + :return: The selected firmware packages + :rtype: list[FirmwareOptdep] + """ + group = MenuItemGroup.from_enum(FirmwareOptdep, sort_items=True, preset=preset) + + result = await Selection[FirmwareOptdep]( + group, + header=tr('Select optional firmware to install (none are pulled in by linux-firmware)'), + allow_skip=True, + allow_reset=True, + multi=True, + ).show() + + match result.type_: + case ResultType.Skip: + return preset + case ResultType.Reset: + return [] + case ResultType.Selection: + return result.get_values() + + async def select_uki(preset: bool = True) -> bool: prompt = tr('Would you like to use unified kernel images?') + '\n' diff --git a/archinstall/lib/global_menu.py b/archinstall/lib/global_menu.py index 1460d3407e..7d5e7fc5f7 100644 --- a/archinstall/lib/global_menu.py +++ b/archinstall/lib/global_menu.py @@ -9,7 +9,7 @@ from archinstall.lib.configuration import save_config from archinstall.lib.disk.disk_menu import DiskLayoutConfigurationMenu from archinstall.lib.general.general_menu import select_hostname, select_ntp, select_timezone -from archinstall.lib.general.system_menu import select_kernel, select_swap +from archinstall.lib.general.system_menu import select_firmware_optdeps, select_kernel, select_swap from archinstall.lib.hardware import SysInfo from archinstall.lib.locale.locale_menu import LocaleMenu from archinstall.lib.menu.abstract_menu import AbstractMenu, SpecialMenuKey @@ -110,6 +110,13 @@ def _get_menu_options(self) -> list[MenuItem]: mandatory=True, key='kernels', ), + MenuItem( + text=tr('Optional firmware'), + value=[], + action=select_firmware_optdeps, + preview_action=self._prev_firmware_optdeps, + key='firmware_optdeps', + ), MenuItem( text=tr('Hostname'), value='archlinux', @@ -438,6 +445,12 @@ def _prev_kernel(self, item: MenuItem) -> str | None: return f'{tr("Kernel")}: {kernel}' return None + def _prev_firmware_optdeps(self, item: MenuItem) -> str | None: + if item.value: + firmware = ', '.join(item.value) + return f'{tr("Optional firmware")}: {firmware}' + return None + def _prev_bootloader_config(self, item: MenuItem) -> str | None: bootloader_config: BootloaderConfiguration | None = item.value if bootloader_config: diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index aeb5c6aea1..4b16f8cf28 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -79,6 +79,7 @@ def __init__( disk_config: DiskLayoutConfiguration, base_packages: list[str] = [], kernels: list[str] | None = None, + firmware: list[str] | None = None, silent: bool = False, ): """ @@ -101,6 +102,10 @@ def __init__( for kernel in self.kernels: self._base_packages.append(kernel) + # linux-firmware pulls in its hard deps only, so the optional ones are + # strapped alongside base or their blobs never reach the target + self._base_packages.extend(firmware or []) + # If using accessibility tools in the live environment, append those to the packages list if accessibility_tools_in_use(): self._base_packages.extend(__accessibility_packages__) diff --git a/archinstall/lib/models/package_types.py b/archinstall/lib/models/package_types.py index e314c30a97..bf1fe1887f 100644 --- a/archinstall/lib/models/package_types.py +++ b/archinstall/lib/models/package_types.py @@ -10,3 +10,20 @@ class Kernel(StrEnum): DEFAULT_KERNEL: Final = Kernel.LINUX + + +class FirmwareOptdep(StrEnum): + """linux-firmware's optional dependencies. + + The metapackage pulls in its hard deps only, so these blobs never reach the + target: hardware that needs one (a Marvell wifi card, a Mellanox NIC) comes + up without firmware and there is nothing in the installer that says so. + Mirrors `pacman -Si linux-firmware` optdepends. + """ + + LIQUIDIO = 'linux-firmware-liquidio' + MARVELL = 'linux-firmware-marvell' + MELLANOX = 'linux-firmware-mellanox' + NFP = 'linux-firmware-nfp' + QCOM = 'linux-firmware-qcom' + QLOGIC = 'linux-firmware-qlogic' diff --git a/archinstall/scripts/guided.py b/archinstall/scripts/guided.py index 627a3b7553..335bc315e8 100644 --- a/archinstall/scripts/guided.py +++ b/archinstall/scripts/guided.py @@ -80,6 +80,7 @@ def perform_installation( mountpoint, disk_config, kernels=config.kernels, + firmware=config.firmware_optdeps, silent=arch_config_handler.args.silent, ) as installation: # Mount all the drives to the desired mountpoint From 4acabbf5b59cd828b48066dbfdf494ae2921f0f2 Mon Sep 17 00:00:00 2001 From: h8d13 Date: Wed, 12 Aug 2026 14:06:58 +0200 Subject: [PATCH 2/5] chore(fmt): shorten comments Signed-off-by: h8d13 --- archinstall/lib/installer.py | 3 +-- archinstall/lib/models/package_types.py | 9 ++++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 4b16f8cf28..36c1a0edd4 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -102,8 +102,7 @@ def __init__( for kernel in self.kernels: self._base_packages.append(kernel) - # linux-firmware pulls in its hard deps only, so the optional ones are - # strapped alongside base or their blobs never reach the target + # Optional firmware is strapped with base so the blobs are in place before the initramfs is generated self._base_packages.extend(firmware or []) # If using accessibility tools in the live environment, append those to the packages list diff --git a/archinstall/lib/models/package_types.py b/archinstall/lib/models/package_types.py index bf1fe1887f..7dbddc7f28 100644 --- a/archinstall/lib/models/package_types.py +++ b/archinstall/lib/models/package_types.py @@ -13,12 +13,11 @@ class Kernel(StrEnum): class FirmwareOptdep(StrEnum): - """linux-firmware's optional dependencies. + """ + The optional dependencies of linux-firmware (pacman -Si linux-firmware). - The metapackage pulls in its hard deps only, so these blobs never reach the - target: hardware that needs one (a Marvell wifi card, a Mellanox NIC) comes - up without firmware and there is nothing in the installer that says so. - Mirrors `pacman -Si linux-firmware` optdepends. + The metapackage installs its hard dependencies only, so these blobs are + never present on the target unless they are requested explicitly. """ LIQUIDIO = 'linux-firmware-liquidio' From a6cf788b89ab4656c6fe28db3a881d862caef17e Mon Sep 17 00:00:00 2001 From: h8d13 Date: Wed, 12 Aug 2026 14:09:57 +0200 Subject: [PATCH 3/5] misc(fmt) Signed-off-by: h8d13 --- archinstall/lib/models/package_types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/archinstall/lib/models/package_types.py b/archinstall/lib/models/package_types.py index 7dbddc7f28..c42c700f36 100644 --- a/archinstall/lib/models/package_types.py +++ b/archinstall/lib/models/package_types.py @@ -14,7 +14,7 @@ class Kernel(StrEnum): class FirmwareOptdep(StrEnum): """ - The optional dependencies of linux-firmware (pacman -Si linux-firmware). + The optional dependencies of linux-firmware. The metapackage installs its hard dependencies only, so these blobs are never present on the target unless they are requested explicitly. From bda45164d50aedc6ac520828f522babba8ea899f Mon Sep 17 00:00:00 2001 From: h8d13 Date: Wed, 12 Aug 2026 14:17:49 +0200 Subject: [PATCH 4/5] idiomatic types Signed-off-by: h8d13 --- archinstall/lib/installer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 36c1a0edd4..567b6f00b0 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -79,7 +79,7 @@ def __init__( disk_config: DiskLayoutConfiguration, base_packages: list[str] = [], kernels: list[str] | None = None, - firmware: list[str] | None = None, + firmware: list[str] = [], silent: bool = False, ): """ @@ -103,7 +103,7 @@ def __init__( self._base_packages.append(kernel) # Optional firmware is strapped with base so the blobs are in place before the initramfs is generated - self._base_packages.extend(firmware or []) + self._base_packages.extend(firmware) # If using accessibility tools in the live environment, append those to the packages list if accessibility_tools_in_use(): From 82a40ffde73062281d9a93c8651938626f3575e9 Mon Sep 17 00:00:00 2001 From: h8d13 Date: Wed, 12 Aug 2026 14:23:29 +0200 Subject: [PATCH 5/5] chore(mypy) Signed-off-by: h8d13 --- archinstall/lib/args.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/archinstall/lib/args.py b/archinstall/lib/args.py index 602fcbd6e1..4c13d6d88d 100644 --- a/archinstall/lib/args.py +++ b/archinstall/lib/args.py @@ -126,6 +126,8 @@ def text(self) -> str: return tr('Hostname') case ArchConfigType.KERNELS: return tr('Kernels') + case ArchConfigType.FIRMWARE_OPTDEPS: + return tr('Optional firmware') case ArchConfigType.NTP: return tr('Automatic time sync (NTP)') case ArchConfigType.TIMEZONE: