From 882556830d6ad275843eecd2b968035ef8394fa0 Mon Sep 17 00:00:00 2001 From: h8d13 Date: Sun, 28 Dec 2025 10:32:27 +0100 Subject: [PATCH 01/14] Add configuration for swap algorithm. Backward compatible implementation --- archinstall/lib/args.py | 2 +- archinstall/lib/global_menu.py | 7 +++- archinstall/lib/installer.py | 8 ++-- archinstall/lib/interactions/system_conf.py | 44 +++++++++++++++++++-- archinstall/scripts/guided.py | 7 +++- examples/config-sample.json | 2 +- 6 files changed, 59 insertions(+), 11 deletions(-) diff --git a/archinstall/lib/args.py b/archinstall/lib/args.py index f654345be9..3d1464dd58 100644 --- a/archinstall/lib/args.py +++ b/archinstall/lib/args.py @@ -72,7 +72,7 @@ class ArchConfig: ntp: bool = True packages: list[str] = field(default_factory=list) parallel_downloads: int = 0 - swap: bool = True + swap: bool | dict[str, Any] = True timezone: str = 'UTC' services: list[str] = field(default_factory=list) custom_commands: list[str] = field(default_factory=list) diff --git a/archinstall/lib/global_menu.py b/archinstall/lib/global_menu.py index 9f7ec10398..0e0f59f708 100644 --- a/archinstall/lib/global_menu.py +++ b/archinstall/lib/global_menu.py @@ -372,7 +372,12 @@ def _prev_disk_config(self, item: MenuItem) -> str | None: def _prev_swap(self, item: MenuItem) -> str | None: if item.value is not None: output = f'{tr("Swap on zram")}: ' - output += tr('Enabled') if item.value else tr('Disabled') + if isinstance(item.value, dict): + output += tr('Enabled') + algo = item.value.get('algo', 'zstd') + output += f'\n{tr("Compression algorithm")}: {algo}' + else: + output += tr('Enabled') if item.value else tr('Disabled') return output return None diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 93829ecc6b..5d4ba921fb 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -975,7 +975,7 @@ def setup_btrfs_snapshot( self._configure_grub_btrfsd(snapshot_type) self.enable_service('grub-btrfsd.service') - def setup_swap(self, kind: str = 'zram') -> None: + def setup_swap(self, kind: str = 'zram', algo: str = 'zstd') -> None: if kind == 'zram': info('Setting up swap on zram') self.pacman.strap('zram-generator') @@ -984,12 +984,12 @@ def setup_swap(self, kind: str = 'zram') -> None: # Convert KB to MB and divide by 2, with minimum of 4096 MB size_mb = max(ram_kb // 2048, 4096) info(f'Zram size: {size_mb} from RAM: {ram_kb}') - # We could use the default example below, but maybe not the best idea: https://github.com/archlinux/archinstall/pull/678#issuecomment-962124813 - # zram_example_location = '/usr/share/doc/zram-generator/zram-generator.conf.example' - # shutil.copy2(f"{self.target}{zram_example_location}", f"{self.target}/usr/lib/systemd/zram-generator.conf") + info(f'Zram compression algorithm: {algo}') + with open(f'{self.target}/etc/systemd/zram-generator.conf', 'w') as zram_conf: zram_conf.write('[zram0]\n') zram_conf.write(f'zram-size = {size_mb}\n') + zram_conf.write(f'compression-algorithm = {algo}\n') self.enable_service('systemd-zram-setup@zram0.service') diff --git a/archinstall/lib/interactions/system_conf.py b/archinstall/lib/interactions/system_conf.py index 0c0e54aaee..8e99db0294 100644 --- a/archinstall/lib/interactions/system_conf.py +++ b/archinstall/lib/interactions/system_conf.py @@ -89,8 +89,16 @@ def select_driver(options: list[GfxDriver] = [], preset: GfxDriver | None = None return result.get_value() -def ask_for_swap(preset: bool = True) -> bool: - if preset: +def ask_for_swap(preset: bool | dict[str, str] = True) -> bool | dict[str, str]: + # Handle backward compatibility + if isinstance(preset, dict): + default_enabled = True + elif isinstance(preset, bool): + default_enabled = preset + else: + default_enabled = True + + if default_enabled: default_item = MenuItem.yes() else: default_item = MenuItem.no() @@ -113,6 +121,36 @@ def ask_for_swap(preset: bool = True) -> bool: case ResultType.Skip: return preset case ResultType.Selection: - return result.item() == MenuItem.yes() + enabled = result.item() == MenuItem.yes() + if not enabled: + return False + + # Ask for compression algorithm + algo_preset = 'zstd' + if isinstance(preset, dict): + algo_preset = preset.get('algo', 'zstd') + + algorithms = ['zstd', 'lzo-rle', 'lzo', 'lz4', 'lz4hc'] + algo_items = [MenuItem(a, value=a) for a in algorithms] + algo_group = MenuItemGroup(algo_items, sort_items=False) + algo_group.set_default_by_value(algo_preset) + algo_group.set_focus_by_value(algo_preset) + + algo_result = SelectMenu[str]( + algo_group, + header=tr('Select zram compression algorithm:') + '\n', + alignment=Alignment.CENTER, + allow_skip=True, + ).run() + + match algo_result.type_: + case ResultType.Skip: + algo = algo_preset + case ResultType.Selection: + algo = algo_result.get_value() + case ResultType.Reset: + algo = 'zstd' + + return {'enabled': True, 'algo': algo} case ResultType.Reset: raise ValueError('Unhandled result type') diff --git a/archinstall/scripts/guided.py b/archinstall/scripts/guided.py index a91aa4272d..075208f486 100644 --- a/archinstall/scripts/guided.py +++ b/archinstall/scripts/guided.py @@ -99,7 +99,12 @@ def perform_installation(mountpoint: Path) -> None: installation.set_mirrors(mirror_config, on_target=True) if config.swap: - installation.setup_swap('zram') + # Handle both boolean and dict swap configurations + if isinstance(config.swap, dict): + algo = config.swap.get('algo', 'zstd') + installation.setup_swap('zram', algo=algo) + else: + installation.setup_swap('zram') if config.bootloader_config and config.bootloader_config.bootloader != Bootloader.NO_BOOTLOADER: if config.bootloader_config.bootloader == Bootloader.Grub and SysInfo.has_uefi(): diff --git a/examples/config-sample.json b/examples/config-sample.json index 30ba5b52d3..aa35eb3a4f 100644 --- a/examples/config-sample.json +++ b/examples/config-sample.json @@ -146,7 +146,7 @@ }, "script": "guided", "silent": false, - "swap": true, + "swap": {"algo": "zstd"}, "timezone": "UTC", "version": "2.8.6" } From 4019f8a78728e8f87882dd6ae7a81fd9c16a81ad Mon Sep 17 00:00:00 2001 From: h8d13 Date: Sun, 28 Dec 2025 10:56:06 +0100 Subject: [PATCH 02/14] Fix interaction to default to Yes and show (default) --- archinstall/lib/interactions/system_conf.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/archinstall/lib/interactions/system_conf.py b/archinstall/lib/interactions/system_conf.py index 8e99db0294..bba9b47d95 100644 --- a/archinstall/lib/interactions/system_conf.py +++ b/archinstall/lib/interactions/system_conf.py @@ -98,15 +98,12 @@ def ask_for_swap(preset: bool | dict[str, str] = True) -> bool | dict[str, str]: else: default_enabled = True - if default_enabled: - default_item = MenuItem.yes() - else: - default_item = MenuItem.no() - prompt = tr('Would you like to use swap on zram?') + '\n' group = MenuItemGroup.yes_no() - group.set_focus_by_value(default_item) + # Set focus to Yes (True) or No (False) based on default_enabled + group.set_default_by_value(default_enabled) + group.set_focus_by_value(default_enabled) result = SelectMenu[bool]( group, From 85170e12932f00bac718948dcb69aabea2910e09 Mon Sep 17 00:00:00 2001 From: h8d13 Date: Sun, 28 Dec 2025 11:05:57 +0100 Subject: [PATCH 03/14] Fix mypy error --- archinstall/lib/interactions/system_conf.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/archinstall/lib/interactions/system_conf.py b/archinstall/lib/interactions/system_conf.py index bba9b47d95..1518a69874 100644 --- a/archinstall/lib/interactions/system_conf.py +++ b/archinstall/lib/interactions/system_conf.py @@ -91,12 +91,12 @@ def select_driver(options: list[GfxDriver] = [], preset: GfxDriver | None = None def ask_for_swap(preset: bool | dict[str, str] = True) -> bool | dict[str, str]: # Handle backward compatibility + default_enabled: bool if isinstance(preset, dict): default_enabled = True - elif isinstance(preset, bool): - default_enabled = preset else: - default_enabled = True + # preset is bool + default_enabled = preset prompt = tr('Would you like to use swap on zram?') + '\n' @@ -140,6 +140,7 @@ def ask_for_swap(preset: bool | dict[str, str] = True) -> bool | dict[str, str]: allow_skip=True, ).run() + algo: str match algo_result.type_: case ResultType.Skip: algo = algo_preset @@ -148,6 +149,6 @@ def ask_for_swap(preset: bool | dict[str, str] = True) -> bool | dict[str, str]: case ResultType.Reset: algo = 'zstd' - return {'enabled': True, 'algo': algo} + return {'algo': algo} case ResultType.Reset: raise ValueError('Unhandled result type') From 20fc31002fd52ecd2c564d67007a81779729bb88 Mon Sep 17 00:00:00 2001 From: h8d13 Date: Sun, 28 Dec 2025 13:48:34 +0100 Subject: [PATCH 04/14] Any -> str, str --- archinstall/lib/args.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/archinstall/lib/args.py b/archinstall/lib/args.py index 3d1464dd58..2e89fd6ba8 100644 --- a/archinstall/lib/args.py +++ b/archinstall/lib/args.py @@ -72,7 +72,7 @@ class ArchConfig: ntp: bool = True packages: list[str] = field(default_factory=list) parallel_downloads: int = 0 - swap: bool | dict[str, Any] = True + swap: bool | dict[str, str] = True timezone: str = 'UTC' services: list[str] = field(default_factory=list) custom_commands: list[str] = field(default_factory=list) From 10c4469c6b3d3c04462802490ad44fc36d0dc7df Mon Sep 17 00:00:00 2001 From: h8d13 Date: Sun, 28 Dec 2025 23:09:27 +0100 Subject: [PATCH 05/14] feedback Enums --- archinstall/lib/args.py | 14 ++++++-- archinstall/lib/global_menu.py | 9 ++--- archinstall/lib/interactions/system_conf.py | 39 +++++++-------------- archinstall/lib/models/application.py | 14 ++++++++ archinstall/scripts/guided.py | 9 ++--- 5 files changed, 43 insertions(+), 42 deletions(-) diff --git a/archinstall/lib/args.py b/archinstall/lib/args.py index 9c45703254..1b717627cd 100644 --- a/archinstall/lib/args.py +++ b/archinstall/lib/args.py @@ -13,7 +13,7 @@ from pydantic.dataclasses import dataclass as p_dataclass from archinstall.lib.crypt import decrypt -from archinstall.lib.models.application import ApplicationConfiguration +from archinstall.lib.models.application import ApplicationConfiguration, ZramAlgorithm, ZramConfig from archinstall.lib.models.authentication import AuthenticationConfiguration from archinstall.lib.models.bootloader import Bootloader, BootloaderConfiguration from archinstall.lib.models.device import DiskEncryption, DiskLayoutConfiguration @@ -72,7 +72,7 @@ class ArchConfig: ntp: bool = True packages: list[str] = field(default_factory=list) parallel_downloads: int = 0 - swap: bool | dict[str, str] = True + swap: ZramConfig = field(default_factory=lambda: ZramConfig(enabled=True)) timezone: str = 'UTC' services: list[str] = field(default_factory=list) custom_commands: list[str] = field(default_factory=list) @@ -211,7 +211,15 @@ def from_config(cls, args_config: dict[str, Any], args: Arguments) -> 'ArchConfi if parallel_downloads := args_config.get('parallel_downloads', 0): arch_config.parallel_downloads = parallel_downloads - arch_config.swap = args_config.get('swap', True) + # Parse swap config - transform bool/dict into ZramConfig + swap_arg = args_config.get('swap', True) + if isinstance(swap_arg, bool): + arch_config.swap = ZramConfig(enabled=swap_arg) + elif isinstance(swap_arg, dict): + algo = swap_arg.get('algo', ZramAlgorithm.ZSTD.value) + arch_config.swap = ZramConfig(enabled=True, algorithm=ZramAlgorithm(algo)) + else: + arch_config.swap = ZramConfig(enabled=True) if timezone := args_config.get('timezone', 'UTC'): arch_config.timezone = timezone diff --git a/archinstall/lib/global_menu.py b/archinstall/lib/global_menu.py index e1feb3b398..54dd20e677 100644 --- a/archinstall/lib/global_menu.py +++ b/archinstall/lib/global_menu.py @@ -372,12 +372,9 @@ def _prev_disk_config(self, item: MenuItem) -> str | None: def _prev_swap(self, item: MenuItem) -> str | None: if item.value is not None: output = f'{tr("Swap on zram")}: ' - if isinstance(item.value, dict): - output += tr('Enabled') - algo = item.value.get('algo', 'zstd') - output += f'\n{tr("Compression algorithm")}: {algo}' - else: - output += tr('Enabled') if item.value else tr('Disabled') + output += tr('Enabled') if item.value.enabled else tr('Disabled') + if item.value.enabled: + output += f'\n{tr("Compression algorithm")}: {item.value.algorithm.value}' return output return None diff --git a/archinstall/lib/interactions/system_conf.py b/archinstall/lib/interactions/system_conf.py index 1518a69874..29ad19c615 100644 --- a/archinstall/lib/interactions/system_conf.py +++ b/archinstall/lib/interactions/system_conf.py @@ -1,5 +1,6 @@ from __future__ import annotations +from archinstall.lib.models.application import ZramAlgorithm, ZramConfig from archinstall.lib.translationhandler import tr from archinstall.tui.curses_menu import SelectMenu from archinstall.tui.menu_item import MenuItem, MenuItemGroup @@ -89,21 +90,12 @@ def select_driver(options: list[GfxDriver] = [], preset: GfxDriver | None = None return result.get_value() -def ask_for_swap(preset: bool | dict[str, str] = True) -> bool | dict[str, str]: - # Handle backward compatibility - default_enabled: bool - if isinstance(preset, dict): - default_enabled = True - else: - # preset is bool - default_enabled = preset - +def ask_for_swap(preset: ZramConfig = ZramConfig(enabled=True)) -> ZramConfig: prompt = tr('Would you like to use swap on zram?') + '\n' group = MenuItemGroup.yes_no() - # Set focus to Yes (True) or No (False) based on default_enabled - group.set_default_by_value(default_enabled) - group.set_focus_by_value(default_enabled) + group.set_default_by_value(preset.enabled) + group.set_focus_by_value(preset.enabled) result = SelectMenu[bool]( group, @@ -120,35 +112,30 @@ def ask_for_swap(preset: bool | dict[str, str] = True) -> bool | dict[str, str]: case ResultType.Selection: enabled = result.item() == MenuItem.yes() if not enabled: - return False + return ZramConfig(enabled=False) # Ask for compression algorithm - algo_preset = 'zstd' - if isinstance(preset, dict): - algo_preset = preset.get('algo', 'zstd') - - algorithms = ['zstd', 'lzo-rle', 'lzo', 'lz4', 'lz4hc'] - algo_items = [MenuItem(a, value=a) for a in algorithms] + algo_items = [MenuItem(a.value, value=a) for a in ZramAlgorithm] algo_group = MenuItemGroup(algo_items, sort_items=False) - algo_group.set_default_by_value(algo_preset) - algo_group.set_focus_by_value(algo_preset) + algo_group.set_default_by_value(preset.algorithm) + algo_group.set_focus_by_value(preset.algorithm) - algo_result = SelectMenu[str]( + algo_result = SelectMenu[ZramAlgorithm]( algo_group, header=tr('Select zram compression algorithm:') + '\n', alignment=Alignment.CENTER, allow_skip=True, ).run() - algo: str + algo: ZramAlgorithm match algo_result.type_: case ResultType.Skip: - algo = algo_preset + algo = preset.algorithm case ResultType.Selection: algo = algo_result.get_value() case ResultType.Reset: - algo = 'zstd' + algo = ZramAlgorithm.ZSTD - return {'algo': algo} + return ZramConfig(enabled=True, algorithm=algo) case ResultType.Reset: raise ValueError('Unhandled result type') diff --git a/archinstall/lib/models/application.py b/archinstall/lib/models/application.py index f5f9e99d8f..1e6b7b8a7e 100644 --- a/archinstall/lib/models/application.py +++ b/archinstall/lib/models/application.py @@ -21,6 +21,14 @@ class PrintServiceConfigSerialization(TypedDict): enabled: bool +class ZramAlgorithm(StrEnum): + ZSTD = 'zstd' + LZO_RLE = 'lzo-rle' + LZO = 'lzo' + LZ4 = 'lz4' + LZ4HC = 'lz4hc' + + class ApplicationSerialization(TypedDict): bluetooth_config: NotRequired[BluetoothConfigSerialization] audio_config: NotRequired[AudioConfigSerialization] @@ -67,6 +75,12 @@ def parse_arg(arg: dict[str, Any]) -> 'PrintServiceConfiguration': return PrintServiceConfiguration(arg['enabled']) +@dataclass +class ZramConfig: + enabled: bool + algorithm: ZramAlgorithm = ZramAlgorithm.ZSTD + + @dataclass class ApplicationConfiguration: bluetooth_config: BluetoothConfiguration | None = None diff --git a/archinstall/scripts/guided.py b/archinstall/scripts/guided.py index 075208f486..9565c688bd 100644 --- a/archinstall/scripts/guided.py +++ b/archinstall/scripts/guided.py @@ -98,13 +98,8 @@ def perform_installation(mountpoint: Path) -> None: if mirror_config := config.mirror_config: installation.set_mirrors(mirror_config, on_target=True) - if config.swap: - # Handle both boolean and dict swap configurations - if isinstance(config.swap, dict): - algo = config.swap.get('algo', 'zstd') - installation.setup_swap('zram', algo=algo) - else: - installation.setup_swap('zram') + if config.swap.enabled: + installation.setup_swap('zram', algo=config.swap.algorithm.value) if config.bootloader_config and config.bootloader_config.bootloader != Bootloader.NO_BOOTLOADER: if config.bootloader_config.bootloader == Bootloader.Grub and SysInfo.has_uefi(): From 20380ac1c06200fe15abaa0b52ac0d8a85c2e792 Mon Sep 17 00:00:00 2001 From: h8d13 Date: Sun, 28 Dec 2025 23:15:10 +0100 Subject: [PATCH 06/14] test file --- tests/test_args.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_args.py b/tests/test_args.py index d3400c83d9..66ce9e2df9 100644 --- a/tests/test_args.py +++ b/tests/test_args.py @@ -7,7 +7,7 @@ from archinstall.default_profiles.profile import GreeterType from archinstall.lib.args import ArchConfig, ArchConfigHandler, Arguments from archinstall.lib.hardware import GfxDriver -from archinstall.lib.models.application import ApplicationConfiguration, Audio, AudioConfiguration, BluetoothConfiguration, PrintServiceConfiguration +from archinstall.lib.models.application import ApplicationConfiguration, Audio, AudioConfiguration, BluetoothConfiguration, PrintServiceConfiguration, ZramConfig from archinstall.lib.models.authentication import AuthenticationConfiguration, U2FLoginConfiguration, U2FLoginMethod from archinstall.lib.models.bootloader import Bootloader, BootloaderConfiguration from archinstall.lib.models.device import DiskLayoutConfiguration, DiskLayoutType @@ -225,7 +225,7 @@ def test_config_file_parsing( ntp=True, packages=['firefox'], parallel_downloads=66, - swap=False, + swap=ZramConfig(enabled=False), timezone='UTC', services=['service_1', 'service_2'], custom_commands=["echo 'Hello, World!'"], From 683c9aaceeceef8701bd183161e0bb3902ec5b6f Mon Sep 17 00:00:00 2001 From: h8d13 Date: Sun, 28 Dec 2025 23:19:41 +0100 Subject: [PATCH 07/14] line length warning --- tests/test_args.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/test_args.py b/tests/test_args.py index 66ce9e2df9..970cfdf4eb 100644 --- a/tests/test_args.py +++ b/tests/test_args.py @@ -7,7 +7,14 @@ from archinstall.default_profiles.profile import GreeterType from archinstall.lib.args import ArchConfig, ArchConfigHandler, Arguments from archinstall.lib.hardware import GfxDriver -from archinstall.lib.models.application import ApplicationConfiguration, Audio, AudioConfiguration, BluetoothConfiguration, PrintServiceConfiguration, ZramConfig +from archinstall.lib.models.application import ( + ApplicationConfiguration, + Audio, + AudioConfiguration, + BluetoothConfiguration, + PrintServiceConfiguration, + ZramConfig, +) from archinstall.lib.models.authentication import AuthenticationConfiguration, U2FLoginConfiguration, U2FLoginMethod from archinstall.lib.models.bootloader import Bootloader, BootloaderConfiguration from archinstall.lib.models.device import DiskLayoutConfiguration, DiskLayoutType From 5772d7e9692d54aaf6fc7467b5cba2e1c91d6029 Mon Sep 17 00:00:00 2001 From: h8d13 Date: Sun, 28 Dec 2025 23:46:17 +0100 Subject: [PATCH 08/14] Renames --- archinstall/lib/args.py | 12 ++++++------ archinstall/lib/interactions/system_conf.py | 8 ++++---- archinstall/lib/models/application.py | 2 +- tests/test_args.py | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/archinstall/lib/args.py b/archinstall/lib/args.py index 1b717627cd..62d222b31c 100644 --- a/archinstall/lib/args.py +++ b/archinstall/lib/args.py @@ -13,7 +13,7 @@ from pydantic.dataclasses import dataclass as p_dataclass from archinstall.lib.crypt import decrypt -from archinstall.lib.models.application import ApplicationConfiguration, ZramAlgorithm, ZramConfig +from archinstall.lib.models.application import ApplicationConfiguration, ZramAlgorithm, ZramConfiguration from archinstall.lib.models.authentication import AuthenticationConfiguration from archinstall.lib.models.bootloader import Bootloader, BootloaderConfiguration from archinstall.lib.models.device import DiskEncryption, DiskLayoutConfiguration @@ -72,7 +72,7 @@ class ArchConfig: ntp: bool = True packages: list[str] = field(default_factory=list) parallel_downloads: int = 0 - swap: ZramConfig = field(default_factory=lambda: ZramConfig(enabled=True)) + swap: ZramConfiguration = field(default_factory=lambda: ZramConfiguration(enabled=True)) timezone: str = 'UTC' services: list[str] = field(default_factory=list) custom_commands: list[str] = field(default_factory=list) @@ -211,15 +211,15 @@ def from_config(cls, args_config: dict[str, Any], args: Arguments) -> 'ArchConfi if parallel_downloads := args_config.get('parallel_downloads', 0): arch_config.parallel_downloads = parallel_downloads - # Parse swap config - transform bool/dict into ZramConfig + # Parse swap config - transform bool/dict into ZramConfiguration swap_arg = args_config.get('swap', True) if isinstance(swap_arg, bool): - arch_config.swap = ZramConfig(enabled=swap_arg) + arch_config.swap = ZramConfiguration(enabled=swap_arg) elif isinstance(swap_arg, dict): algo = swap_arg.get('algo', ZramAlgorithm.ZSTD.value) - arch_config.swap = ZramConfig(enabled=True, algorithm=ZramAlgorithm(algo)) + arch_config.swap = ZramConfiguration(enabled=True, algorithm=ZramAlgorithm(algo)) else: - arch_config.swap = ZramConfig(enabled=True) + arch_config.swap = ZramConfiguration(enabled=True) if timezone := args_config.get('timezone', 'UTC'): arch_config.timezone = timezone diff --git a/archinstall/lib/interactions/system_conf.py b/archinstall/lib/interactions/system_conf.py index 29ad19c615..54ad9bc3d2 100644 --- a/archinstall/lib/interactions/system_conf.py +++ b/archinstall/lib/interactions/system_conf.py @@ -1,6 +1,6 @@ from __future__ import annotations -from archinstall.lib.models.application import ZramAlgorithm, ZramConfig +from archinstall.lib.models.application import ZramAlgorithm, ZramConfiguration from archinstall.lib.translationhandler import tr from archinstall.tui.curses_menu import SelectMenu from archinstall.tui.menu_item import MenuItem, MenuItemGroup @@ -90,7 +90,7 @@ def select_driver(options: list[GfxDriver] = [], preset: GfxDriver | None = None return result.get_value() -def ask_for_swap(preset: ZramConfig = ZramConfig(enabled=True)) -> ZramConfig: +def ask_for_swap(preset: ZramConfiguration = ZramConfiguration(enabled=True)) -> ZramConfiguration: prompt = tr('Would you like to use swap on zram?') + '\n' group = MenuItemGroup.yes_no() @@ -112,7 +112,7 @@ def ask_for_swap(preset: ZramConfig = ZramConfig(enabled=True)) -> ZramConfig: case ResultType.Selection: enabled = result.item() == MenuItem.yes() if not enabled: - return ZramConfig(enabled=False) + return ZramConfiguration(enabled=False) # Ask for compression algorithm algo_items = [MenuItem(a.value, value=a) for a in ZramAlgorithm] @@ -136,6 +136,6 @@ def ask_for_swap(preset: ZramConfig = ZramConfig(enabled=True)) -> ZramConfig: case ResultType.Reset: algo = ZramAlgorithm.ZSTD - return ZramConfig(enabled=True, algorithm=algo) + return ZramConfiguration(enabled=True, algorithm=algo) case ResultType.Reset: raise ValueError('Unhandled result type') diff --git a/archinstall/lib/models/application.py b/archinstall/lib/models/application.py index 1e6b7b8a7e..cec53f6caf 100644 --- a/archinstall/lib/models/application.py +++ b/archinstall/lib/models/application.py @@ -76,7 +76,7 @@ def parse_arg(arg: dict[str, Any]) -> 'PrintServiceConfiguration': @dataclass -class ZramConfig: +class ZramConfiguration: enabled: bool algorithm: ZramAlgorithm = ZramAlgorithm.ZSTD diff --git a/tests/test_args.py b/tests/test_args.py index 970cfdf4eb..275ffd86f0 100644 --- a/tests/test_args.py +++ b/tests/test_args.py @@ -13,7 +13,7 @@ AudioConfiguration, BluetoothConfiguration, PrintServiceConfiguration, - ZramConfig, + ZramConfiguration, ) from archinstall.lib.models.authentication import AuthenticationConfiguration, U2FLoginConfiguration, U2FLoginMethod from archinstall.lib.models.bootloader import Bootloader, BootloaderConfiguration @@ -232,7 +232,7 @@ def test_config_file_parsing( ntp=True, packages=['firefox'], parallel_downloads=66, - swap=ZramConfig(enabled=False), + swap=ZramConfiguration(enabled=False), timezone='UTC', services=['service_1', 'service_2'], custom_commands=["echo 'Hello, World!'"], From f61aeb6f2850c58251e5a45486855c0e648573d0 Mon Sep 17 00:00:00 2001 From: h8d13 Date: Sun, 28 Dec 2025 23:50:24 +0100 Subject: [PATCH 09/14] Fix default values in TUI menu for display --- archinstall/lib/interactions/system_conf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/archinstall/lib/interactions/system_conf.py b/archinstall/lib/interactions/system_conf.py index 54ad9bc3d2..8d2d39d31f 100644 --- a/archinstall/lib/interactions/system_conf.py +++ b/archinstall/lib/interactions/system_conf.py @@ -94,7 +94,7 @@ def ask_for_swap(preset: ZramConfiguration = ZramConfiguration(enabled=True)) -> prompt = tr('Would you like to use swap on zram?') + '\n' group = MenuItemGroup.yes_no() - group.set_default_by_value(preset.enabled) + group.set_default_by_value(True) group.set_focus_by_value(preset.enabled) result = SelectMenu[bool]( @@ -117,7 +117,7 @@ def ask_for_swap(preset: ZramConfiguration = ZramConfiguration(enabled=True)) -> # Ask for compression algorithm algo_items = [MenuItem(a.value, value=a) for a in ZramAlgorithm] algo_group = MenuItemGroup(algo_items, sort_items=False) - algo_group.set_default_by_value(preset.algorithm) + algo_group.set_default_by_value(ZramAlgorithm.ZSTD) algo_group.set_focus_by_value(preset.algorithm) algo_result = SelectMenu[ZramAlgorithm]( From 15c3a290c2f8b8532a3e8ccd08f9362d78ce4195 Mon Sep 17 00:00:00 2001 From: h8d13 Date: Mon, 29 Dec 2025 09:44:49 +0100 Subject: [PATCH 10/14] Address feedback --- archinstall/lib/args.py | 6 +++--- archinstall/lib/installer.py | 7 ++++--- archinstall/lib/interactions/system_conf.py | 5 +---- archinstall/lib/models/application.py | 2 +- archinstall/scripts/guided.py | 2 +- examples/config-sample.json | 5 ++++- 6 files changed, 14 insertions(+), 13 deletions(-) diff --git a/archinstall/lib/args.py b/archinstall/lib/args.py index 62d222b31c..0f4ab03acc 100644 --- a/archinstall/lib/args.py +++ b/archinstall/lib/args.py @@ -72,7 +72,7 @@ class ArchConfig: ntp: bool = True packages: list[str] = field(default_factory=list) parallel_downloads: int = 0 - swap: ZramConfiguration = field(default_factory=lambda: ZramConfiguration(enabled=True)) + swap: ZramConfiguration = ZramConfiguration(enabled=True) timezone: str = 'UTC' services: list[str] = field(default_factory=list) custom_commands: list[str] = field(default_factory=list) @@ -212,14 +212,14 @@ def from_config(cls, args_config: dict[str, Any], args: Arguments) -> 'ArchConfi arch_config.parallel_downloads = parallel_downloads # Parse swap config - transform bool/dict into ZramConfiguration - swap_arg = args_config.get('swap', True) + swap_arg = args_config.get('swap', ZramConfiguration(enabled=True)) if isinstance(swap_arg, bool): arch_config.swap = ZramConfiguration(enabled=swap_arg) elif isinstance(swap_arg, dict): algo = swap_arg.get('algo', ZramAlgorithm.ZSTD.value) arch_config.swap = ZramConfiguration(enabled=True, algorithm=ZramAlgorithm(algo)) else: - arch_config.swap = ZramConfiguration(enabled=True) + arch_config.swap = swap_arg if timezone := args_config.get('timezone', 'UTC'): arch_config.timezone = timezone diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 7b19222c9b..6c793e89a4 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -16,6 +16,7 @@ from archinstall.lib.disk.device_handler import device_handler from archinstall.lib.disk.fido import Fido2 from archinstall.lib.disk.utils import get_lsblk_by_mountpoint, get_lsblk_info +from archinstall.lib.models.application import ZramAlgorithm from archinstall.lib.models.device import ( DiskEncryption, DiskLayoutConfiguration, @@ -990,7 +991,7 @@ def setup_btrfs_snapshot( self._configure_grub_btrfsd(snapshot_type) self.enable_service('grub-btrfsd.service') - def setup_swap(self, kind: str = 'zram', algo: str = 'zstd') -> None: + def setup_swap(self, kind: str = 'zram', algo: ZramAlgorithm = ZramAlgorithm.ZSTD) -> None: if kind == 'zram': info('Setting up swap on zram') self.pacman.strap('zram-generator') @@ -999,12 +1000,12 @@ def setup_swap(self, kind: str = 'zram', algo: str = 'zstd') -> None: # Convert KB to MB and divide by 2, with minimum of 4096 MB size_mb = max(ram_kb // 2048, 4096) info(f'Zram size: {size_mb} from RAM: {ram_kb}') - info(f'Zram compression algorithm: {algo}') + info(f'Zram compression algorithm: {algo.value}') with open(f'{self.target}/etc/systemd/zram-generator.conf', 'w') as zram_conf: zram_conf.write('[zram0]\n') zram_conf.write(f'zram-size = {size_mb}\n') - zram_conf.write(f'compression-algorithm = {algo}\n') + zram_conf.write(f'compression-algorithm = {algo.value}\n') self.enable_service('systemd-zram-setup@zram0.service') diff --git a/archinstall/lib/interactions/system_conf.py b/archinstall/lib/interactions/system_conf.py index 8d2d39d31f..534e1a7d81 100644 --- a/archinstall/lib/interactions/system_conf.py +++ b/archinstall/lib/interactions/system_conf.py @@ -115,8 +115,7 @@ def ask_for_swap(preset: ZramConfiguration = ZramConfiguration(enabled=True)) -> return ZramConfiguration(enabled=False) # Ask for compression algorithm - algo_items = [MenuItem(a.value, value=a) for a in ZramAlgorithm] - algo_group = MenuItemGroup(algo_items, sort_items=False) + algo_group = MenuItemGroup.from_enum(ZramAlgorithm, sort_items=False) algo_group.set_default_by_value(ZramAlgorithm.ZSTD) algo_group.set_focus_by_value(preset.algorithm) @@ -133,8 +132,6 @@ def ask_for_swap(preset: ZramConfiguration = ZramConfiguration(enabled=True)) -> algo = preset.algorithm case ResultType.Selection: algo = algo_result.get_value() - case ResultType.Reset: - algo = ZramAlgorithm.ZSTD return ZramConfiguration(enabled=True, algorithm=algo) case ResultType.Reset: diff --git a/archinstall/lib/models/application.py b/archinstall/lib/models/application.py index cec53f6caf..699779f12d 100644 --- a/archinstall/lib/models/application.py +++ b/archinstall/lib/models/application.py @@ -75,7 +75,7 @@ def parse_arg(arg: dict[str, Any]) -> 'PrintServiceConfiguration': return PrintServiceConfiguration(arg['enabled']) -@dataclass +@dataclass(frozen=True) class ZramConfiguration: enabled: bool algorithm: ZramAlgorithm = ZramAlgorithm.ZSTD diff --git a/archinstall/scripts/guided.py b/archinstall/scripts/guided.py index 9565c688bd..95c07ce40e 100644 --- a/archinstall/scripts/guided.py +++ b/archinstall/scripts/guided.py @@ -99,7 +99,7 @@ def perform_installation(mountpoint: Path) -> None: installation.set_mirrors(mirror_config, on_target=True) if config.swap.enabled: - installation.setup_swap('zram', algo=config.swap.algorithm.value) + installation.setup_swap('zram', algo=config.swap.algorithm) if config.bootloader_config and config.bootloader_config.bootloader != Bootloader.NO_BOOTLOADER: if config.bootloader_config.bootloader == Bootloader.Grub and SysInfo.has_uefi(): diff --git a/examples/config-sample.json b/examples/config-sample.json index aa35eb3a4f..bc4cd2d924 100644 --- a/examples/config-sample.json +++ b/examples/config-sample.json @@ -146,7 +146,10 @@ }, "script": "guided", "silent": false, - "swap": {"algo": "zstd"}, + "swap": { + "enabled": true, + "algorithm": "zstd" + }, "timezone": "UTC", "version": "2.8.6" } From a2ca8b6386f57665df444e5b036fc907613d3d5a Mon Sep 17 00:00:00 2001 From: h8d13 Date: Mon, 29 Dec 2025 09:52:49 +0100 Subject: [PATCH 11/14] More feedback, really appreciate it. --- archinstall/lib/args.py | 13 +++---------- archinstall/lib/models/application.py | 9 +++++++++ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/archinstall/lib/args.py b/archinstall/lib/args.py index 0f4ab03acc..ae87cbd972 100644 --- a/archinstall/lib/args.py +++ b/archinstall/lib/args.py @@ -13,7 +13,7 @@ from pydantic.dataclasses import dataclass as p_dataclass from archinstall.lib.crypt import decrypt -from archinstall.lib.models.application import ApplicationConfiguration, ZramAlgorithm, ZramConfiguration +from archinstall.lib.models.application import ApplicationConfiguration, ZramConfiguration from archinstall.lib.models.authentication import AuthenticationConfiguration from archinstall.lib.models.bootloader import Bootloader, BootloaderConfiguration from archinstall.lib.models.device import DiskEncryption, DiskLayoutConfiguration @@ -211,15 +211,8 @@ def from_config(cls, args_config: dict[str, Any], args: Arguments) -> 'ArchConfi if parallel_downloads := args_config.get('parallel_downloads', 0): arch_config.parallel_downloads = parallel_downloads - # Parse swap config - transform bool/dict into ZramConfiguration - swap_arg = args_config.get('swap', ZramConfiguration(enabled=True)) - if isinstance(swap_arg, bool): - arch_config.swap = ZramConfiguration(enabled=swap_arg) - elif isinstance(swap_arg, dict): - algo = swap_arg.get('algo', ZramAlgorithm.ZSTD.value) - arch_config.swap = ZramConfiguration(enabled=True, algorithm=ZramAlgorithm(algo)) - else: - arch_config.swap = swap_arg + swap_arg = args_config.get('swap', True) + arch_config.swap = ZramConfiguration.parse_arg(swap_arg) if timezone := args_config.get('timezone', 'UTC'): arch_config.timezone = timezone diff --git a/archinstall/lib/models/application.py b/archinstall/lib/models/application.py index 699779f12d..42a1390a34 100644 --- a/archinstall/lib/models/application.py +++ b/archinstall/lib/models/application.py @@ -80,6 +80,15 @@ class ZramConfiguration: enabled: bool algorithm: ZramAlgorithm = ZramAlgorithm.ZSTD + @staticmethod + def parse_arg(arg: bool | dict[str, Any]) -> 'ZramConfiguration': + if isinstance(arg, bool): + return ZramConfiguration(enabled=arg) + + enabled = arg.get('enabled', True) + algo = arg.get('algorithm', arg.get('algo', ZramAlgorithm.ZSTD.value)) + return ZramConfiguration(enabled=enabled, algorithm=ZramAlgorithm(algo)) + @dataclass class ApplicationConfiguration: From 89350c73fa4ee88ff375dd45ab9b4d689387e1ac Mon Sep 17 00:00:00 2001 From: h8d13 Date: Mon, 29 Dec 2025 10:00:38 +0100 Subject: [PATCH 12/14] Adapt to use same | None = None pattern --- archinstall/lib/args.py | 6 +++--- archinstall/scripts/guided.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/archinstall/lib/args.py b/archinstall/lib/args.py index ae87cbd972..7934e772ba 100644 --- a/archinstall/lib/args.py +++ b/archinstall/lib/args.py @@ -67,12 +67,12 @@ class ArchConfig: bootloader_config: BootloaderConfiguration | None = None app_config: ApplicationConfiguration | None = None auth_config: AuthenticationConfiguration | None = None + swap: ZramConfiguration | None = None hostname: str = 'archlinux' kernels: list[str] = field(default_factory=lambda: ['linux']) ntp: bool = True packages: list[str] = field(default_factory=list) parallel_downloads: int = 0 - swap: ZramConfiguration = ZramConfiguration(enabled=True) timezone: str = 'UTC' services: list[str] = field(default_factory=list) custom_commands: list[str] = field(default_factory=list) @@ -211,8 +211,8 @@ def from_config(cls, args_config: dict[str, Any], args: Arguments) -> 'ArchConfi if parallel_downloads := args_config.get('parallel_downloads', 0): arch_config.parallel_downloads = parallel_downloads - swap_arg = args_config.get('swap', True) - arch_config.swap = ZramConfiguration.parse_arg(swap_arg) + if swap_arg := args_config.get('swap'): + arch_config.swap = ZramConfiguration.parse_arg(swap_arg) if timezone := args_config.get('timezone', 'UTC'): arch_config.timezone = timezone diff --git a/archinstall/scripts/guided.py b/archinstall/scripts/guided.py index 95c07ce40e..70e6cb89ab 100644 --- a/archinstall/scripts/guided.py +++ b/archinstall/scripts/guided.py @@ -98,7 +98,7 @@ def perform_installation(mountpoint: Path) -> None: if mirror_config := config.mirror_config: installation.set_mirrors(mirror_config, on_target=True) - if config.swap.enabled: + if config.swap and config.swap.enabled: installation.setup_swap('zram', algo=config.swap.algorithm) if config.bootloader_config and config.bootloader_config.bootloader != Bootloader.NO_BOOTLOADER: From b50eec944e2012a4039ac2b663fd9923074c8309 Mon Sep 17 00:00:00 2001 From: h8d13 Date: Mon, 29 Dec 2025 10:04:01 +0100 Subject: [PATCH 13/14] Pytests --- archinstall/lib/args.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/archinstall/lib/args.py b/archinstall/lib/args.py index 7934e772ba..286429a05b 100644 --- a/archinstall/lib/args.py +++ b/archinstall/lib/args.py @@ -211,7 +211,8 @@ def from_config(cls, args_config: dict[str, Any], args: Arguments) -> 'ArchConfi if parallel_downloads := args_config.get('parallel_downloads', 0): arch_config.parallel_downloads = parallel_downloads - if swap_arg := args_config.get('swap'): + swap_arg = args_config.get('swap') + if swap_arg is not None: arch_config.swap = ZramConfiguration.parse_arg(swap_arg) if timezone := args_config.get('timezone', 'UTC'): From ea5358b6efda981e841786b3fba74838aeffa5c1 Mon Sep 17 00:00:00 2001 From: h8d13 Date: Mon, 29 Dec 2025 22:00:02 +0100 Subject: [PATCH 14/14] Add missing import for Zram --- archinstall/lib/global_menu.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/archinstall/lib/global_menu.py b/archinstall/lib/global_menu.py index 54dd20e677..09b5ffd998 100644 --- a/archinstall/lib/global_menu.py +++ b/archinstall/lib/global_menu.py @@ -3,7 +3,7 @@ from typing import override from archinstall.lib.disk.disk_menu import DiskLayoutConfigurationMenu -from archinstall.lib.models.application import ApplicationConfiguration +from archinstall.lib.models.application import ApplicationConfiguration, ZramConfiguration from archinstall.lib.models.authentication import AuthenticationConfiguration from archinstall.lib.models.device import DiskLayoutConfiguration, DiskLayoutType, EncryptionType, FilesystemType, PartitionModification from archinstall.lib.packages import list_available_packages @@ -80,7 +80,7 @@ def _get_menu_options(self) -> list[MenuItem]: ), MenuItem( text=tr('Swap'), - value=True, + value=ZramConfiguration(enabled=True), action=ask_for_swap, preview_action=self._prev_swap, key='swap',