From f00717ff6fd1c72d61b6928444fbf26a3f5e0e64 Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Mon, 2 May 2022 21:01:50 +1000 Subject: Fix #1106 (#1119) * Fix #1106 * flake8 * flake8 Co-authored-by: Daniel Girtler --- profiles/desktop.py | 37 +++++++++++++++++++++---------------- profiles/i3.py | 29 +++++++++++++++-------------- profiles/server.py | 17 ++++++++++------- profiles/sway.py | 16 +++++++++++----- profiles/xorg.py | 8 ++++++-- 5 files changed, 63 insertions(+), 44 deletions(-) (limited to 'profiles') diff --git a/profiles/desktop.py b/profiles/desktop.py index bd3353e8..977fe2de 100644 --- a/profiles/desktop.py +++ b/profiles/desktop.py @@ -1,5 +1,6 @@ # A desktop environment selector. import archinstall +from archinstall import log is_top_level_profile = True @@ -38,29 +39,33 @@ __supported__ = [ ] -def _prep_function(*args, **kwargs): +def _prep_function(*args, **kwargs) -> bool: """ Magic function called by the importing installer before continuing any further. It also avoids executing any other code in this stage. So it's a safe way to ask the user for more input before any other installer steps start. """ - desktop = archinstall.Menu('Select your desired desktop environment', __supported__, skip=False).run() + desktop = archinstall.Menu('Select your desired desktop environment', __supported__).run() - # Temporarily store the selected desktop profile - # in a session-safe location, since this module will get reloaded - # the next time it gets executed. - if not archinstall.storage.get('_desktop_profile', None): - archinstall.storage['_desktop_profile'] = desktop - if not archinstall.arguments.get('desktop-environment', None): - archinstall.arguments['desktop-environment'] = desktop - profile = archinstall.Profile(None, desktop) - # Loading the instructions with a custom namespace, ensures that a __name__ comparison is never triggered. - with profile.load_instructions(namespace=f"{desktop}.py") as imported: - if hasattr(imported, '_prep_function'): - return imported._prep_function() - else: - print(f"Deprecated (??): {desktop} profile has no _prep_function() anymore") + if desktop: + # Temporarily store the selected desktop profile + # in a session-safe location, since this module will get reloaded + # the next time it gets executed. + if not archinstall.storage.get('_desktop_profile', None): + archinstall.storage['_desktop_profile'] = desktop + if not archinstall.arguments.get('desktop-environment', None): + archinstall.arguments['desktop-environment'] = desktop + profile = archinstall.Profile(None, desktop) + # Loading the instructions with a custom namespace, ensures that a __name__ comparison is never triggered. + with profile.load_instructions(namespace=f"{desktop}.py") as imported: + if hasattr(imported, '_prep_function'): + return imported._prep_function() + else: + log(f"Deprecated (??): {desktop} profile has no _prep_function() anymore") + exit(1) + + return False if __name__ == 'desktop': diff --git a/profiles/i3.py b/profiles/i3.py index 24956209..3283848e 100644 --- a/profiles/i3.py +++ b/profiles/i3.py @@ -27,20 +27,21 @@ def _prep_function(*args, **kwargs): supported_configurations = ['i3-wm', 'i3-gaps'] - desktop = archinstall.Menu('Select your desired configuration', supported_configurations, skip=False).run() - - # Temporarily store the selected desktop profile - # in a session-safe location, since this module will get reloaded - # the next time it gets executed. - archinstall.storage['_i3_configuration'] = desktop - - # i3 requires a functioning Xorg installation. - profile = archinstall.Profile(None, 'xorg') - with profile.load_instructions(namespace='xorg.py') as imported: - if hasattr(imported, '_prep_function'): - return imported._prep_function() - else: - print('Deprecated (??): xorg profile has no _prep_function() anymore') + desktop = archinstall.Menu('Select your desired configuration', supported_configurations).run() + + if desktop: + # Temporarily store the selected desktop profile + # in a session-safe location, since this module will get reloaded + # the next time it gets executed. + archinstall.storage['_i3_configuration'] = desktop + + # i3 requires a functioning Xorg installation. + profile = archinstall.Profile(None, 'xorg') + with profile.load_instructions(namespace='xorg.py') as imported: + if hasattr(imported, '_prep_function'): + return imported._prep_function() + else: + print('Deprecated (??): xorg profile has no _prep_function() anymore') if __name__ == 'i3': diff --git a/profiles/server.py b/profiles/server.py index c4f35f7b..bbeece12 100644 --- a/profiles/server.py +++ b/profiles/server.py @@ -26,15 +26,18 @@ def _prep_function(*args, **kwargs): Magic function called by the importing installer before continuing any further. """ - if not archinstall.storage.get('_selected_servers', None): - servers = archinstall.Menu( - 'Choose which servers to install, if none then a minimal installation wil be done', available_servers, - multi=True - ).run() - + servers = archinstall.Menu( + 'Choose which servers to install, if none then a minimal installation wil be done', + available_servers, + preset_values=archinstall.storage.get('_selected_servers', []), + multi=True + ).run() + + if servers: archinstall.storage['_selected_servers'] = servers + return True - return True + return False if __name__ == 'server': diff --git a/profiles/sway.py b/profiles/sway.py index 32d626d7..e9c71b79 100644 --- a/profiles/sway.py +++ b/profiles/sway.py @@ -18,7 +18,9 @@ __packages__ = [ def _check_driver() -> bool: - if "nvidia" in archinstall.storage.get("gfx_driver_packages", None): + packages = archinstall.storage.get("gfx_driver_packages", []) + + if packages and "nvidia" in packages: prompt = 'The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?' choice = archinstall.Menu(prompt, ['yes', 'no'], default_option='no').run() if choice == 'no': @@ -34,11 +36,15 @@ def _prep_function(*args, **kwargs): other code in this stage. So it's a safe way to ask the user for more input before any other installer steps start. """ - archinstall.storage["gfx_driver_packages"] = archinstall.select_driver(force_ask=True) - if not _check_driver(): - return _prep_function(args, kwargs) + driver = archinstall.select_driver() - return True + if driver: + archinstall.storage["gfx_driver_packages"] = driver + if not _check_driver(): + return _prep_function(args, kwargs) + return True + + return False # Ensures that this code only gets executed if executed diff --git a/profiles/xorg.py b/profiles/xorg.py index 33d2aa4c..237e4350 100644 --- a/profiles/xorg.py +++ b/profiles/xorg.py @@ -25,12 +25,16 @@ def _prep_function(*args, **kwargs): for more input before any other installer steps start. """ - archinstall.storage["gfx_driver_packages"] = archinstall.select_driver() + driver = archinstall.select_driver() + + if driver: + archinstall.storage["gfx_driver_packages"] = driver + return True # TODO: Add language section and/or merge it with the locale selected # earlier in for instance guided.py installer. - return True + return False # Ensures that this code only gets executed if executed -- cgit v1.2.3-70-g09d2 From bcd810d2d20e657d96f36e0007facff71e9532bc Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Thu, 5 May 2022 20:48:01 +1000 Subject: Fix 1117 (#1126) * Fix 1117 * Update * flake8 Co-authored-by: Daniel Girtler --- archinstall/lib/disk/user_guides.py | 36 +++++++++++++--------- archinstall/lib/menu/global_menu.py | 4 +-- archinstall/lib/menu/menu.py | 14 +++++++++ archinstall/lib/user_interaction/disk_conf.py | 4 +-- archinstall/lib/user_interaction/general_conf.py | 8 ++--- .../lib/user_interaction/manage_users_conf.py | 8 ++--- .../lib/user_interaction/partitioning_conf.py | 12 +++++--- .../lib/user_interaction/subvolume_config.py | 2 +- archinstall/lib/user_interaction/system_conf.py | 18 +++++------ archinstall/lib/user_interaction/utils.py | 11 +++---- examples/guided.py | 6 ++-- examples/swiss.py | 10 +++--- profiles/sway.py | 5 +-- 13 files changed, 79 insertions(+), 59 deletions(-) (limited to 'profiles') diff --git a/archinstall/lib/disk/user_guides.py b/archinstall/lib/disk/user_guides.py index 63ec1d9b..77da52e4 100644 --- a/archinstall/lib/disk/user_guides.py +++ b/archinstall/lib/disk/user_guides.py @@ -5,6 +5,7 @@ from typing import Optional, Dict, Any, List, TYPE_CHECKING # https://stackoverflow.com/a/39757388/929999 if TYPE_CHECKING: from .blockdevice import BlockDevice + _: Any from .helpers import sort_block_devices_based_on_performance, select_largest_device, select_disk_larger_than_or_close_to from ..hardware import has_uefi @@ -26,13 +27,13 @@ def suggest_single_disk_layout(block_device :BlockDevice, compression = False if default_filesystem == 'btrfs': - prompt = 'Would you like to use BTRFS subvolumes with a default structure?' - choice = Menu(prompt, ['yes', 'no'], skip=False, default_option='yes').run() - using_subvolumes = choice == 'yes' + prompt = str(_('Would you like to use BTRFS subvolumes with a default structure?')) + choice = Menu(prompt, Menu.yes_no(), skip=False, default_option=Menu.yes()).run() + using_subvolumes = choice == Menu.yes() - prompt = 'Would you like to use BTRFS compression?' - choice = Menu(prompt, ['yes', 'no'], skip=False, default_option='yes').run() - compression = choice == 'yes' + prompt = str(_('Would you like to use BTRFS compression?')) + choice = Menu(prompt, Menu.yes_no(), skip=False, default_option=Menu.yes()).run() + compression = choice == Menu.yes() layout = { block_device.path : { @@ -87,9 +88,9 @@ def suggest_single_disk_layout(block_device :BlockDevice, layout[block_device.path]['partitions'][-1]['start'] = '513MiB' if not using_subvolumes and block_device.size >= MIN_SIZE_TO_ALLOW_HOME_PART: - prompt = 'Would you like to create a separate partition for /home?' - choice = Menu(prompt, ['yes', 'no'], skip=False, default_option='yes').run() - using_home_partition = choice == 'yes' + prompt = str(_('Would you like to create a separate partition for /home?')) + choice = Menu(prompt, Menu.yes_no(), skip=False, default_option=Menu.yes()).run() + using_home_partition = choice == Menu.yes() # Set a size for / (/root) if using_subvolumes or block_device.size < MIN_SIZE_TO_ALLOW_HOME_PART or not using_home_partition: @@ -138,9 +139,7 @@ def suggest_single_disk_layout(block_device :BlockDevice, return layout -def suggest_multi_disk_layout(block_devices :List[BlockDevice], - default_filesystem :Optional[str] = None, - advanced_options :bool = False) -> Dict[str, Any]: +def suggest_multi_disk_layout(block_devices :List[BlockDevice], default_filesystem :Optional[str] = None, advanced_options :bool = False): if not default_filesystem: from ..user_interaction import ask_for_main_filesystem_format @@ -158,6 +157,13 @@ def suggest_multi_disk_layout(block_devices :List[BlockDevice], home_device = select_largest_device(block_devices, gigabytes=MIN_SIZE_TO_ALLOW_HOME_PART) root_device = select_disk_larger_than_or_close_to(block_devices, gigabytes=ARCH_LINUX_INSTALLED_SIZE, filter_out=[home_device]) + if home_device is None or root_device is None: + text = _('The selected drives do not have the minimum capacity required for an automatic suggestion\n') + text += _('Minimum capacity for /home partition: {}GB\n').format(MIN_SIZE_TO_ALLOW_HOME_PART) + text += _('Minimum capacity for Arch Linux partition: {}GB').format(ARCH_LINUX_INSTALLED_SIZE) + Menu(str(text), [str(_('Continue'))], skip=False).run() + return None + compression = False if default_filesystem == 'btrfs': @@ -165,9 +171,9 @@ def suggest_multi_disk_layout(block_devices :List[BlockDevice], # choice = Menu(prompt, ['yes', 'no'], skip=False, default_option='yes').run() # using_subvolumes = choice == 'yes' - prompt = 'Would you like to use BTRFS compression?' - choice = Menu(prompt, ['yes', 'no'], skip=False, default_option='yes').run() - compression = choice == 'yes' + prompt = str(_('Would you like to use BTRFS compression?')) + choice = Menu(prompt, Menu.yes_no(), skip=False, default_option=Menu.yes()).run() + compression = choice == Menu.yes() log(f"Suggesting multi-disk-layout using {len(block_devices)} disks, where {root_device} will be /root and {home_device} will be /home", level=logging.DEBUG) diff --git a/archinstall/lib/menu/global_menu.py b/archinstall/lib/menu/global_menu.py index 63b8c03e..b3c5c6a2 100644 --- a/archinstall/lib/menu/global_menu.py +++ b/archinstall/lib/menu/global_menu.py @@ -262,9 +262,9 @@ class GlobalMenu(GeneralMenu): "Do you wish to continue?" ).format(storage['MOUNT_POINT']) - choice = Menu(prompt, ['yes', 'no'], default_option='yes').run() + choice = Menu(prompt, Menu.yes_no(), default_option=Menu.yes()).run() - if choice == 'no': + if choice == Menu.no(): exit(1) return harddrives diff --git a/archinstall/lib/menu/menu.py b/archinstall/lib/menu/menu.py index d254e0f9..48ea4635 100644 --- a/archinstall/lib/menu/menu.py +++ b/archinstall/lib/menu/menu.py @@ -12,7 +12,21 @@ import logging if TYPE_CHECKING: _: Any + class Menu(TerminalMenu): + + @classmethod + def yes(cls): + return str(_('yes')) + + @classmethod + def no(cls): + return str(_('no')) + + @classmethod + def yes_no(cls): + return [cls.yes(), cls.no()] + def __init__( self, title :str, diff --git a/archinstall/lib/user_interaction/disk_conf.py b/archinstall/lib/user_interaction/disk_conf.py index a51f10b8..e84f647a 100644 --- a/archinstall/lib/user_interaction/disk_conf.py +++ b/archinstall/lib/user_interaction/disk_conf.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any, Dict, TYPE_CHECKING +from typing import Any, Dict, TYPE_CHECKING, Optional from .partitioning_conf import manage_new_and_existing_partitions, get_default_partition_layout from ..disk import BlockDevice @@ -36,7 +36,7 @@ def select_individual_blockdevice_usage(block_devices: list) -> Dict[str, Any]: return result -def select_disk_layout(block_devices: list, advanced_options=False) -> Dict[str, Any]: +def select_disk_layout(block_devices: list, advanced_options=False) -> Optional[Dict[str, Any]]: wipe_mode = str(_('Wipe all selected drives and use a best-effort default partition layout')) custome_mode = str(_('Select what to do with each individual drive (followed by partition usage)')) modes = [wipe_mode, custome_mode] diff --git a/archinstall/lib/user_interaction/general_conf.py b/archinstall/lib/user_interaction/general_conf.py index c42e9e27..78f6b460 100644 --- a/archinstall/lib/user_interaction/general_conf.py +++ b/archinstall/lib/user_interaction/general_conf.py @@ -22,11 +22,11 @@ def ask_ntp(preset: bool = True) -> bool: prompt = str(_('Would you like to use automatic time synchronization (NTP) with the default time servers?\n')) prompt += str(_('Hardware time and other post-configuration steps might be required in order for NTP to work.\nFor more information, please check the Arch wiki')) if preset: - preset_val = 'yes' + preset_val = Menu.yes() else: - preset_val = 'no' - choice = Menu(prompt, ['yes', 'no'], skip=False, preset_values=preset_val, default_option='yes').run() - return False if choice == 'no' else True + preset_val = Menu.no() + choice = Menu(prompt, Menu.yes_no(), skip=False, preset_values=preset_val, default_option=Menu.yes()).run() + return False if choice == Menu.no() else True def ask_hostname(preset: str = None) -> str: diff --git a/archinstall/lib/user_interaction/manage_users_conf.py b/archinstall/lib/user_interaction/manage_users_conf.py index d69ccce9..ea909f35 100644 --- a/archinstall/lib/user_interaction/manage_users_conf.py +++ b/archinstall/lib/user_interaction/manage_users_conf.py @@ -109,11 +109,11 @@ class UserList(ListManager): sudoer = False else: sudoer = False - sudo_choice = Menu(str(_('Should {} be a superuser (sudoer)?')).format(userid), ['yes', 'no'], + sudo_choice = Menu(str(_('Should {} be a superuser (sudoer)?')).format(userid), Menu.yes_no(), skip=False, - preset_values='yes' if sudoer else 'no', - default_option='no').run() - sudoer = True if sudo_choice == 'yes' else False + preset_values=Menu.yes() if sudoer else Menu.no(), + default_option=Menu.no()).run() + sudoer = True if sudo_choice == Menu.yes() else False password = get_password(prompt=str(_('Password for user "{}": ').format(userid))) diff --git a/archinstall/lib/user_interaction/partitioning_conf.py b/archinstall/lib/user_interaction/partitioning_conf.py index 2182c6b3..afca1cef 100644 --- a/archinstall/lib/user_interaction/partitioning_conf.py +++ b/archinstall/lib/user_interaction/partitioning_conf.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import List, Any, Dict, Union, TYPE_CHECKING, Callable +from typing import List, Any, Dict, Union, TYPE_CHECKING, Callable, Optional from ..menu import Menu from ..output import log @@ -97,8 +97,10 @@ def select_partition(title :str, partitions :List[Partition], multiple :bool = F return None -def get_default_partition_layout(block_devices: Union['BlockDevice', List['BlockDevice']], - advanced_options: bool = False) -> Dict[str, Any]: +def get_default_partition_layout( + block_devices: Union['BlockDevice', List['BlockDevice']], + advanced_options: bool = False +) -> Optional[Dict[str, Any]]: from ..disk import suggest_single_disk_layout, suggest_multi_disk_layout if len(block_devices) == 1: @@ -224,9 +226,9 @@ def manage_new_and_existing_partitions(block_device: 'BlockDevice') -> Dict[str, if len(block_device_struct["partitions"]): prompt = _('{} contains queued partitions, this will remove those, are you sure?').format(block_device) - choice = Menu(prompt, ['yes', 'no'], default_option='no').run() + choice = Menu(prompt, Menu.yes_no(), default_option=Menu.no()).run() - if choice == 'no': + if choice == Menu.no(): continue block_device_struct.update(suggest_single_disk_layout(block_device)[block_device.path]) diff --git a/archinstall/lib/user_interaction/subvolume_config.py b/archinstall/lib/user_interaction/subvolume_config.py index adbb7430..bb159a8b 100644 --- a/archinstall/lib/user_interaction/subvolume_config.py +++ b/archinstall/lib/user_interaction/subvolume_config.py @@ -1,4 +1,4 @@ -from typing import Any, Dict +from typing import Dict from ..menu.list_manager import ListManager from ..menu.selection_menu import Selector, GeneralMenu diff --git a/archinstall/lib/user_interaction/system_conf.py b/archinstall/lib/user_interaction/system_conf.py index 5d4c80dc..17c093c1 100644 --- a/archinstall/lib/user_interaction/system_conf.py +++ b/archinstall/lib/user_interaction/system_conf.py @@ -102,9 +102,9 @@ def select_driver(options: Dict[str, Any] = AVAILABLE_GFX_DRIVERS) -> str: def ask_for_bootloader(advanced_options: bool = False, preset: str = None) -> str: if preset == 'systemd-bootctl': - preset_val = 'systemd-boot' if advanced_options else 'no' + preset_val = 'systemd-boot' if advanced_options else Menu.no() elif preset == 'grub-install': - preset_val = 'grub' if advanced_options else 'yes' + preset_val = 'grub' if advanced_options else Menu.yes() else: preset_val = preset @@ -112,11 +112,11 @@ def ask_for_bootloader(advanced_options: bool = False, preset: str = None) -> st if has_uefi(): if not advanced_options: bootloader_choice = Menu(_('Would you like to use GRUB as a bootloader instead of systemd-boot?'), - ['yes', 'no'], + Menu.yes_no(), preset_values=preset_val, - default_option='no').run() + default_option=Menu.no()).run() - if bootloader_choice == "yes": + if bootloader_choice == Menu.yes(): bootloader = "grub-install" else: # We use the common names for the bootloader as the selection, and map it back to the expected values. @@ -135,9 +135,9 @@ def ask_for_bootloader(advanced_options: bool = False, preset: str = None) -> st def ask_for_swap(preset: bool = True) -> bool: if preset: - preset_val = 'yes' + preset_val = Menu.yes() else: - preset_val = 'no' + preset_val = Menu.no() prompt = _('Would you like to use swap on zram?') - choice = Menu(prompt, ['yes', 'no'], default_option='yes', preset_values=preset_val).run() - return False if choice == 'no' else True + choice = Menu(prompt, Menu.yes_no(), default_option=Menu.yes(), preset_values=preset_val).run() + return False if choice == Menu.no() else True diff --git a/archinstall/lib/user_interaction/utils.py b/archinstall/lib/user_interaction/utils.py index 48b55e8c..59f2dfbc 100644 --- a/archinstall/lib/user_interaction/utils.py +++ b/archinstall/lib/user_interaction/utils.py @@ -28,12 +28,9 @@ def check_password_strong(passwd: str) -> bool: symbol_count += 40 if symbol_count**len(passwd) < 10e20: - - prompt = _("The password you are using seems to be weak,") - prompt += _("are you sure you want to use it?") - - choice = Menu(prompt, ["yes", "no"], default_option="yes").run() - return choice == "yes" + prompt = str(_("The password you are using seems to be weak, are you sure you want to use it?")) + choice = Menu(prompt, Menu.yes_no(), default_option=Menu.yes()).run() + return choice == Menu.yes() return True @@ -84,7 +81,7 @@ def do_countdown() -> bool: if SIG_TRIGGER: prompt = _('Do you really want to abort?') - choice = Menu(prompt, ['yes', 'no'], skip=False).run() + choice = Menu(prompt, Menu.yes_no(), skip=False).run() if choice == 'yes': exit(0) diff --git a/examples/guided.py b/examples/guided.py index 15226668..1cee499d 100644 --- a/examples/guided.py +++ b/examples/guided.py @@ -3,7 +3,7 @@ import os import time import archinstall -from archinstall import ConfigurationOutput +from archinstall import ConfigurationOutput, Menu from archinstall.lib.models.network_configuration import NetworkConfigurationHandler if archinstall.arguments.get('help'): @@ -258,8 +258,8 @@ def perform_installation(mountpoint): installation.log("For post-installation tips, see https://wiki.archlinux.org/index.php/Installation_guide#Post-installation", fg="yellow") if not archinstall.arguments.get('silent'): prompt = 'Would you like to chroot into the newly created installation and perform post-installation configuration?' - choice = archinstall.Menu(prompt, ['yes', 'no'], default_option='yes').run() - if choice == 'yes': + choice = Menu(prompt, Menu.yes_no(), default_option=Menu.yes()).run() + if choice == Menu.yes(): try: installation.drop_to_shell() except: diff --git a/examples/swiss.py b/examples/swiss.py index baf7b618..6d357191 100644 --- a/examples/swiss.py +++ b/examples/swiss.py @@ -20,7 +20,7 @@ import pathlib from typing import TYPE_CHECKING, Any import archinstall -from archinstall import ConfigurationOutput, NetworkConfigurationHandler +from archinstall import ConfigurationOutput, NetworkConfigurationHandler, Menu if TYPE_CHECKING: _: Any @@ -38,8 +38,8 @@ TODO exec con return parameter """ def select_activate_NTP(): prompt = "Would you like to use automatic time synchronization (NTP) with the default time servers? [Y/n]: " - choice = archinstall.Menu(prompt, ['yes', 'no'], default_option='yes').run() - if choice == 'yes': + choice = Menu(prompt, Menu.yes_no(), default_option=Menu.yes()).run() + if choice == Menu.yes(): return True else: return False @@ -480,8 +480,8 @@ def perform_installation(mountpoint, mode): installation.log("For post-installation tips, see https://wiki.archlinux.org/index.php/Installation_guide#Post-installation", fg="yellow") if not archinstall.arguments.get('silent'): prompt = 'Would you like to chroot into the newly created installation and perform post-installation configuration?' - choice = archinstall.Menu(prompt, ['yes', 'no'], default_option='yes').run() - if choice == 'yes': + choice = Menu(prompt, Menu.yes_no(), default_option=Menu.yes()).run() + if choice == Menu.yes(): try: installation.drop_to_shell() except: diff --git a/profiles/sway.py b/profiles/sway.py index e9c71b79..0819db95 100644 --- a/profiles/sway.py +++ b/profiles/sway.py @@ -1,5 +1,6 @@ # A desktop environment using "Sway" import archinstall +from archinstall import Menu is_top_level_profile = False @@ -22,8 +23,8 @@ def _check_driver() -> bool: if packages and "nvidia" in packages: prompt = 'The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?' - choice = archinstall.Menu(prompt, ['yes', 'no'], default_option='no').run() - if choice == 'no': + choice = Menu(prompt, Menu.yes_no(), default_option=Menu.no()).run() + if choice == Menu.no(): return False return True -- cgit v1.2.3-70-g09d2 From 20ffebac50478554a7582de5e5c1d8b4504ea8be Mon Sep 17 00:00:00 2001 From: Alexmelman88 <99257010+Alexmelman88@users.noreply.github.com> Date: Mon, 9 May 2022 10:58:06 +0300 Subject: Updated base.pot, added new translation strings from *.py files, updated ru locale (#1146) * Create base.po * Add files via upload * Delete base.po * Add files via upload * Delete base.mo * Add files via upload * Update base.pot * Update base.po * Delete base.mo * Add files via upload * Delete base.mo * Add files via upload * Update base.po * Delete base.mo * Add files via upload * Add files via upload * Add files via upload * Add files via upload * Add files via upload * Add files via upload * Add files via upload * Update desktop.py * Update minimal.py * Update xorg.py * Update server.py * Update desktop.py * Update server.py * Update guided.py * Add files via upload * Add files via upload * Update guided.py * Add files via upload * Add files via upload --- archinstall/locales/base.pot | 404 ++++++++++++++++++----------- archinstall/locales/ru/LC_MESSAGES/base.mo | Bin 25612 -> 30101 bytes archinstall/locales/ru/LC_MESSAGES/base.po | 318 ++++++++++++++++++++--- examples/guided.py | 4 +- profiles/desktop.py | 4 +- profiles/minimal.py | 2 +- profiles/server.py | 6 +- profiles/xorg.py | 2 +- 8 files changed, 544 insertions(+), 196 deletions(-) (limited to 'profiles') diff --git a/archinstall/locales/base.pot b/archinstall/locales/base.pot index cd5d9df8..4be7b458 100644 --- a/archinstall/locales/base.pot +++ b/archinstall/locales/base.pot @@ -8,31 +8,30 @@ msgstr "" msgid "[!] A log file has been created here: {} {}" msgstr "" -#: lib/installer.py:145 lib/installer.py:153 lib/installer.py:153 +#: lib/installer.py:145 lib/installer.py:153 lib/installer.py:155 msgid "" " Please submit this issue (and file) to https://github.com/archlinux/" "archinstall/issues" msgstr "" #: lib/user_interaction.py:83 lib/user_interaction/utils.py:86 -#: lib/user_interaction/utils.py:86 +#: lib/user_interaction/utils.py:83 msgid "Do you really want to abort?" msgstr "" #: lib/user_interaction.py:101 lib/user_interaction.py:104 -#: lib/user_interaction/utils.py:53 lib/user_interaction/utils.py:53 +#: lib/user_interaction/utils.py:53 lib/user_interaction/utils.py:50 msgid "And one more time for verification: " msgstr "" #: lib/user_interaction.py:272 lib/user_interaction.py:275 #: lib/user_interaction/system_conf.py:142 -#: lib/user_interaction/system_conf.py:142 +#: lib/user_interaction/system_conf.py:141 msgid "Would you like to use swap on zram?" msgstr "" #: lib/user_interaction.py:285 lib/user_interaction.py:288 #: lib/user_interaction/general_conf.py:33 -#: lib/user_interaction/general_conf.py:33 msgid "Desired hostname for the installation: " msgstr "" @@ -42,7 +41,7 @@ msgstr "" #: lib/user_interaction.py:310 lib/user_interaction.py:313 #: lib/user_interaction/manage_users_conf.py:167 -#: lib/user_interaction/manage_users_conf.py:167 +#: lib/user_interaction/manage_users_conf.py:164 msgid "Any additional users to install (leave blank for no users): " msgstr "" @@ -52,35 +51,32 @@ msgstr "" #: lib/user_interaction.py:340 lib/user_interaction.py:343 #: lib/user_interaction.py:346 lib/user_interaction/general_conf.py:41 -#: lib/user_interaction/general_conf.py:41 msgid "Select a timezone" msgstr "" #: lib/user_interaction.py:354 lib/user_interaction.py:357 #: lib/user_interaction.py:360 lib/user_interaction.py:359 #: lib/user_interaction/system_conf.py:115 -#: lib/user_interaction/system_conf.py:115 +#: lib/user_interaction/system_conf.py:114 msgid "Would you like to use GRUB as a bootloader instead of systemd-boot?" msgstr "" #: lib/user_interaction.py:364 lib/user_interaction.py:367 #: lib/user_interaction.py:370 lib/user_interaction.py:369 #: lib/user_interaction/system_conf.py:125 -#: lib/user_interaction/system_conf.py:125 +#: lib/user_interaction/system_conf.py:124 msgid "Choose a bootloader" msgstr "" #: lib/user_interaction.py:380 lib/user_interaction.py:383 #: lib/user_interaction.py:386 lib/user_interaction.py:385 #: lib/user_interaction/general_conf.py:53 -#: lib/user_interaction/general_conf.py:53 msgid "Choose an audio server" msgstr "" #: lib/user_interaction.py:391 lib/user_interaction.py:394 #: lib/user_interaction.py:397 lib/user_interaction.py:396 #: lib/user_interaction/general_conf.py:138 -#: lib/user_interaction/general_conf.py:138 msgid "" "Only packages such as base, base-devel, linux, linux-firmware, efibootmgr " "and optional profile packages are installed." @@ -89,7 +85,6 @@ msgstr "" #: lib/user_interaction.py:392 lib/user_interaction.py:395 #: lib/user_interaction.py:398 lib/user_interaction.py:397 #: lib/user_interaction/general_conf.py:139 -#: lib/user_interaction/general_conf.py:139 msgid "" "If you desire a web browser, such as firefox or chromium, you may specify it " "in the following prompt." @@ -98,7 +93,6 @@ msgstr "" #: lib/user_interaction.py:396 lib/user_interaction.py:399 #: lib/user_interaction.py:402 lib/user_interaction.py:401 #: lib/user_interaction/general_conf.py:143 -#: lib/user_interaction/general_conf.py:143 msgid "" "Write additional packages to install (space separated, leave blank to skip): " msgstr "" @@ -106,14 +100,14 @@ msgstr "" #: lib/user_interaction.py:419 lib/user_interaction.py:422 #: lib/user_interaction.py:425 lib/user_interaction.py:424 #: lib/user_interaction/network_conf.py:25 -#: lib/user_interaction/network_conf.py:25 +#: lib/user_interaction/network_conf.py:125 msgid "Copy ISO network configuration to installation" msgstr "" #: lib/user_interaction.py:420 lib/user_interaction.py:423 #: lib/user_interaction.py:426 lib/user_interaction.py:425 #: lib/user_interaction/network_conf.py:26 -#: lib/user_interaction/network_conf.py:26 +#: lib/user_interaction/network_conf.py:126 msgid "" "Use NetworkManager (necessary to configure internet graphically in GNOME and " "KDE)" @@ -122,14 +116,14 @@ msgstr "" #: lib/user_interaction.py:428 lib/user_interaction.py:431 #: lib/user_interaction.py:434 lib/user_interaction.py:433 #: lib/user_interaction/network_conf.py:43 -#: lib/user_interaction/network_conf.py:43 +#: lib/user_interaction/network_conf.py:139 msgid "Select one network interface to configure" msgstr "" #: lib/user_interaction.py:441 lib/user_interaction.py:444 #: lib/user_interaction.py:447 lib/user_interaction.py:446 #: lib/user_interaction/network_conf.py:70 -#: lib/user_interaction/network_conf.py:70 +#: lib/user_interaction/network_conf.py:77 msgid "" "Select which mode to configure for \"{}\" or skip to use default mode \"{}\"" msgstr "" @@ -137,35 +131,34 @@ msgstr "" #: lib/user_interaction.py:446 lib/user_interaction.py:449 #: lib/user_interaction.py:452 lib/user_interaction.py:451 #: lib/user_interaction/network_conf.py:75 -#: lib/user_interaction/network_conf.py:75 +#: lib/user_interaction/network_conf.py:82 msgid "Enter the IP and subnet for {} (example: 192.168.0.5/24): " msgstr "" #: lib/user_interaction.py:461 lib/user_interaction.py:464 #: lib/user_interaction.py:467 lib/user_interaction.py:466 #: lib/user_interaction/network_conf.py:86 -#: lib/user_interaction/network_conf.py:86 +#: lib/user_interaction/network_conf.py:93 msgid "Enter your gateway (router) IP address or leave blank for none: " msgstr "" #: lib/user_interaction.py:476 lib/user_interaction.py:479 #: lib/user_interaction.py:482 lib/user_interaction.py:481 #: lib/user_interaction/network_conf.py:102 -#: lib/user_interaction/network_conf.py:102 +#: lib/user_interaction/network_conf.py:108 msgid "Enter your DNS servers (space separated, blank for none): " msgstr "" #: lib/user_interaction.py:510 lib/user_interaction.py:513 #: lib/user_interaction.py:516 lib/user_interaction.py:515 -#: lib/user_interaction/disk_conf.py:23 lib/user_interaction/disk_conf.py:23 +#: lib/user_interaction/disk_conf.py:23 msgid "Select which filesystem your main partition should use" msgstr "" #: lib/user_interaction.py:556 lib/user_interaction.py:559 #: lib/user_interaction.py:562 lib/user_interaction.py:561 -#: lib/user_interaction/partitioning_conf.py:64 -#: lib/user_interaction/partitioning_conf.py:63 #: lib/user_interaction/partitioning_conf.py:63 +#: lib/user_interaction/partitioning_conf.py:64 msgid "Current partition layout" msgstr "" @@ -173,7 +166,7 @@ msgstr "" #: lib/user_interaction.py:617 lib/user_interaction.py:620 #: lib/user_interaction.py:619 lib/user_interaction/partitioning_conf.py:145 #: lib/user_interaction/partitioning_conf.py:146 -#: lib/user_interaction/partitioning_conf.py:146 +#: lib/user_interaction/partitioning_conf.py:163 msgid "" "Select what to do with\n" "{}" @@ -188,8 +181,8 @@ msgstr "" #: lib/user_interaction/partitioning_conf.py:255 #: lib/user_interaction/partitioning_conf.py:165 #: lib/user_interaction/partitioning_conf.py:260 -#: lib/user_interaction/partitioning_conf.py:165 -#: lib/user_interaction/partitioning_conf.py:260 +#: lib/user_interaction/partitioning_conf.py:182 +#: lib/user_interaction/partitioning_conf.py:293 msgid "Enter a desired filesystem type for the partition" msgstr "" @@ -197,7 +190,7 @@ msgstr "" #: lib/user_interaction.py:636 lib/user_interaction.py:639 #: lib/user_interaction.py:638 lib/user_interaction/partitioning_conf.py:164 #: lib/user_interaction/partitioning_conf.py:167 -#: lib/user_interaction/partitioning_conf.py:167 +#: lib/user_interaction/partitioning_conf.py:187 msgid "Enter the start sector (percentage or block number, default: {}): " msgstr "" @@ -205,7 +198,7 @@ msgstr "" #: lib/user_interaction.py:645 lib/user_interaction.py:648 #: lib/user_interaction.py:647 lib/user_interaction/partitioning_conf.py:174 #: lib/user_interaction/partitioning_conf.py:177 -#: lib/user_interaction/partitioning_conf.py:177 +#: lib/user_interaction/partitioning_conf.py:197 msgid "" "Enter the end sector of the partition (percentage or block number, ex: {}): " msgstr "" @@ -214,7 +207,7 @@ msgstr "" #: lib/user_interaction.py:671 lib/user_interaction.py:674 #: lib/user_interaction.py:673 lib/user_interaction/partitioning_conf.py:203 #: lib/user_interaction/partitioning_conf.py:208 -#: lib/user_interaction/partitioning_conf.py:208 +#: lib/user_interaction/partitioning_conf.py:228 msgid "{} contains queued partitions, this will remove those, are you sure?" msgstr "" @@ -222,7 +215,7 @@ msgstr "" #: lib/user_interaction.py:684 lib/user_interaction.py:687 #: lib/user_interaction.py:686 lib/user_interaction/partitioning_conf.py:216 #: lib/user_interaction/partitioning_conf.py:221 -#: lib/user_interaction/partitioning_conf.py:221 +#: lib/user_interaction/partitioning_conf.py:242 msgid "" "{}\n" "\n" @@ -233,7 +226,7 @@ msgstr "" #: lib/user_interaction.py:692 lib/user_interaction.py:695 #: lib/user_interaction.py:694 lib/user_interaction/partitioning_conf.py:226 #: lib/user_interaction/partitioning_conf.py:231 -#: lib/user_interaction/partitioning_conf.py:231 +#: lib/user_interaction/partitioning_conf.py:264 msgid "" "{}\n" "\n" @@ -244,7 +237,7 @@ msgstr "" #: lib/user_interaction.py:696 lib/user_interaction.py:699 #: lib/user_interaction.py:698 lib/user_interaction/partitioning_conf.py:231 #: lib/user_interaction/partitioning_conf.py:236 -#: lib/user_interaction/partitioning_conf.py:236 +#: lib/user_interaction/partitioning_conf.py:269 msgid "" " * Partition mount-points are relative to inside the installation, the boot " "would be /boot as an example." @@ -254,7 +247,7 @@ msgstr "" #: lib/user_interaction.py:697 lib/user_interaction.py:700 #: lib/user_interaction.py:699 lib/user_interaction/partitioning_conf.py:233 #: lib/user_interaction/partitioning_conf.py:238 -#: lib/user_interaction/partitioning_conf.py:238 +#: lib/user_interaction/partitioning_conf.py:271 msgid "Select where to mount partition (leave blank to remove mountpoint): " msgstr "" @@ -262,7 +255,7 @@ msgstr "" #: lib/user_interaction.py:708 lib/user_interaction.py:711 #: lib/user_interaction.py:710 lib/user_interaction/partitioning_conf.py:244 #: lib/user_interaction/partitioning_conf.py:249 -#: lib/user_interaction/partitioning_conf.py:249 +#: lib/user_interaction/partitioning_conf.py:282 msgid "" "{}\n" "\n" @@ -273,7 +266,7 @@ msgstr "" #: lib/user_interaction.py:727 lib/user_interaction.py:730 #: lib/user_interaction.py:729 lib/user_interaction/partitioning_conf.py:265 #: lib/user_interaction/partitioning_conf.py:270 -#: lib/user_interaction/partitioning_conf.py:270 +#: lib/user_interaction/partitioning_conf.py:302 msgid "" "{}\n" "\n" @@ -284,7 +277,8 @@ msgstr "" #: lib/user_interaction.py:735 lib/user_interaction.py:738 #: lib/user_interaction.py:737 lib/user_interaction/partitioning_conf.py:274 #: lib/user_interaction/partitioning_conf.py:279 -#: lib/user_interaction/partitioning_conf.py:279 +#: lib/user_interaction/partitioning_conf.py:250 +#: lib/user_interaction/partitioning_conf.py:311 msgid "" "{}\n" "\n" @@ -295,7 +289,7 @@ msgstr "" #: lib/user_interaction.py:742 lib/user_interaction.py:745 #: lib/user_interaction.py:744 lib/user_interaction/partitioning_conf.py:282 #: lib/user_interaction/partitioning_conf.py:287 -#: lib/user_interaction/partitioning_conf.py:287 +#: lib/user_interaction/partitioning_conf.py:319 msgid "" "{}\n" "\n" @@ -306,7 +300,7 @@ msgstr "" #: lib/user_interaction.py:749 lib/user_interaction.py:752 #: lib/user_interaction.py:751 lib/user_interaction/partitioning_conf.py:289 #: lib/user_interaction/partitioning_conf.py:294 -#: lib/user_interaction/partitioning_conf.py:294 +#: lib/user_interaction/partitioning_conf.py:326 msgid "Enter a desired filesystem type for the partition: " msgstr "" @@ -318,7 +312,7 @@ msgstr "" #: lib/menu/selection_menu.py:17 lib/user_interaction/general_conf.py:105 #: lib/user_interaction/global_menu.py:43 lib/menu/selection_menu.py:53 #: lib/menu/selection_menu.py:463 lib/menu/global_menu.py:43 -#: lib/menu/selection_menu.py:53 lib/menu/global_menu.py:43 +#: lib/menu/selection_menu.py:20 #: lib/user_interaction/general_conf.py:105 msgid "Select Archinstall language" msgstr "" @@ -326,14 +320,12 @@ msgstr "" #: lib/user_interaction.py:765 lib/user_interaction.py:772 #: lib/user_interaction.py:775 lib/user_interaction.py:778 #: lib/user_interaction.py:777 lib/user_interaction/disk_conf.py:40 -#: lib/user_interaction/disk_conf.py:40 msgid "Wipe all selected drives and use a best-effort default partition layout" msgstr "" #: lib/user_interaction.py:766 lib/user_interaction.py:773 #: lib/user_interaction.py:776 lib/user_interaction.py:779 #: lib/user_interaction.py:778 lib/user_interaction/disk_conf.py:41 -#: lib/user_interaction/disk_conf.py:41 msgid "" "Select what to do with each individual drive (followed by partition usage)" msgstr "" @@ -341,7 +333,7 @@ msgstr "" #: lib/user_interaction.py:769 lib/user_interaction.py:770 #: lib/user_interaction.py:777 lib/user_interaction.py:780 #: lib/user_interaction.py:783 lib/user_interaction.py:782 -#: lib/user_interaction/disk_conf.py:45 lib/user_interaction/disk_conf.py:45 +#: lib/user_interaction/disk_conf.py:45 lib/user_interaction/disk_conf.py:44 msgid "Select what you wish to do with the selected block devices" msgstr "" @@ -349,7 +341,6 @@ msgstr "" #: lib/user_interaction.py:830 lib/user_interaction.py:833 #: lib/user_interaction.py:836 lib/user_interaction.py:835 #: lib/user_interaction/general_conf.py:126 -#: lib/user_interaction/general_conf.py:126 msgid "" "This is a list of pre-programmed profiles, they might make it easier to " "install things like desktop environments" @@ -359,7 +350,6 @@ msgstr "" #: lib/user_interaction.py:854 lib/user_interaction.py:857 #: lib/user_interaction.py:860 lib/user_interaction.py:859 #: lib/user_interaction/general_conf.py:71 -#: lib/user_interaction/general_conf.py:71 msgid "Select Keyboard layout" msgstr "" @@ -367,7 +357,6 @@ msgstr "" #: lib/user_interaction.py:869 lib/user_interaction.py:872 #: lib/user_interaction.py:875 lib/user_interaction.py:874 #: lib/user_interaction/general_conf.py:92 -#: lib/user_interaction/general_conf.py:92 msgid "Select one of the regions to download packages from" msgstr "" @@ -375,7 +364,6 @@ msgstr "" #: lib/user_interaction.py:891 lib/user_interaction.py:894 #: lib/user_interaction.py:897 lib/user_interaction.py:896 #: lib/user_interaction/system_conf.py:52 -#: lib/user_interaction/system_conf.py:52 msgid "Select one or more hard drives to use and configure" msgstr "" @@ -383,7 +371,6 @@ msgstr "" #: lib/user_interaction.py:918 lib/user_interaction.py:921 #: lib/user_interaction.py:924 lib/user_interaction.py:923 #: lib/user_interaction/system_conf.py:80 -#: lib/user_interaction/system_conf.py:80 msgid "" "For the best compatibility with your AMD hardware, you may want to use " "either the all open-source or AMD / ATI options." @@ -393,7 +380,6 @@ msgstr "" #: lib/user_interaction.py:920 lib/user_interaction.py:923 #: lib/user_interaction.py:926 lib/user_interaction.py:925 #: lib/user_interaction/system_conf.py:84 -#: lib/user_interaction/system_conf.py:84 msgid "" "For the best compatibility with your Intel hardware, you may want to use " "either the all open-source or Intel options.\n" @@ -403,7 +389,6 @@ msgstr "" #: lib/user_interaction.py:922 lib/user_interaction.py:925 #: lib/user_interaction.py:928 lib/user_interaction.py:927 #: lib/user_interaction/system_conf.py:88 -#: lib/user_interaction/system_conf.py:88 msgid "" "For the best compatibility with your Nvidia hardware, you may want to use " "the Nvidia proprietary driver.\n" @@ -413,7 +398,7 @@ msgstr "" #: lib/user_interaction.py:925 lib/user_interaction.py:928 #: lib/user_interaction.py:931 lib/user_interaction.py:930 #: lib/user_interaction/system_conf.py:92 -#: lib/user_interaction/system_conf.py:92 +#: lib/user_interaction/system_conf.py:91 msgid "" "\n" "\n" @@ -424,7 +409,7 @@ msgstr "" #: lib/user_interaction.py:929 lib/user_interaction.py:932 #: lib/user_interaction.py:935 lib/user_interaction.py:934 #: lib/user_interaction/system_conf.py:96 -#: lib/user_interaction/system_conf.py:96 +#: lib/user_interaction/system_conf.py:95 msgid "All open-source (default)" msgstr "" @@ -432,7 +417,6 @@ msgstr "" #: lib/user_interaction.py:948 lib/user_interaction.py:951 #: lib/user_interaction.py:954 lib/user_interaction.py:953 #: lib/user_interaction/system_conf.py:28 -#: lib/user_interaction/system_conf.py:28 msgid "Choose which kernels to use or leave blank for default \"{}\"" msgstr "" @@ -440,7 +424,6 @@ msgstr "" #: lib/user_interaction.py:962 lib/user_interaction.py:965 #: lib/user_interaction.py:968 lib/user_interaction.py:967 #: lib/user_interaction/locale_conf.py:16 -#: lib/user_interaction/locale_conf.py:16 msgid "Choose which locale language to use" msgstr "" @@ -448,7 +431,6 @@ msgstr "" #: lib/user_interaction.py:976 lib/user_interaction.py:979 #: lib/user_interaction.py:982 lib/user_interaction.py:981 #: lib/user_interaction/locale_conf.py:29 -#: lib/user_interaction/locale_conf.py:29 msgid "Choose which locale encoding to use" msgstr "" @@ -456,7 +438,6 @@ msgstr "" #: lib/user_interaction.py:1017 lib/user_interaction.py:1020 #: lib/user_interaction.py:1023 lib/user_interaction.py:1022 #: lib/user_interaction/backwards_compatible_conf.py:49 -#: lib/user_interaction/backwards_compatible_conf.py:49 msgid "Select one of the values shown below: " msgstr "" @@ -464,27 +445,26 @@ msgstr "" #: lib/user_interaction.py:1058 lib/user_interaction.py:1061 #: lib/user_interaction.py:1064 lib/user_interaction.py:1063 #: lib/user_interaction/backwards_compatible_conf.py:88 -#: lib/user_interaction/backwards_compatible_conf.py:88 msgid "Select one or more of the options below: " msgstr "" #: lib/disk/filesystem.py:86 lib/disk/filesystem.py:87 -#: lib/disk/filesystem.py:87 +#: lib/disk/filesystem.py:85 msgid "Adding partition...." msgstr "" #: lib/disk/filesystem.py:139 lib/disk/filesystem.py:141 -#: lib/disk/filesystem.py:142 lib/disk/filesystem.py:142 +#: lib/disk/filesystem.py:142 lib/disk/filesystem.py:140 msgid "" "You need to enter a valid fs-type in order to continue. See `man parted` for " "valid fs-type's." msgstr "" -#: lib/profiles.py:89 lib/profiles.py:90 lib/profiles.py:90 +#: lib/profiles.py:89 lib/profiles.py:90 msgid "Error: Listing profiles on URL \"{}\" resulted in:" msgstr "" -#: lib/profiles.py:92 lib/profiles.py:93 lib/profiles.py:93 +#: lib/profiles.py:92 lib/profiles.py:93 msgid "Error: Could not decode \"{}\" result as JSON:" msgstr "" @@ -492,7 +472,7 @@ msgstr "" #: lib/menu/selection_menu.py:148 lib/menu/selection_menu.py:412 #: lib/menu/selection_menu.py:420 lib/user_interaction/global_menu.py:48 #: lib/menu/selection_menu.py:468 lib/menu/global_menu.py:48 -#: lib/menu/global_menu.py:48 +#: lib/menu/global_menu.py:47 msgid "Select keyboard layout" msgstr "" @@ -500,7 +480,7 @@ msgstr "" #: lib/menu/selection_menu.py:151 lib/menu/selection_menu.py:415 #: lib/menu/selection_menu.py:423 lib/user_interaction/global_menu.py:51 #: lib/menu/selection_menu.py:471 lib/menu/global_menu.py:51 -#: lib/menu/global_menu.py:51 +#: lib/menu/global_menu.py:50 msgid "Select mirror region" msgstr "" @@ -508,7 +488,7 @@ msgstr "" #: lib/menu/selection_menu.py:156 lib/menu/selection_menu.py:420 #: lib/menu/selection_menu.py:428 lib/user_interaction/global_menu.py:56 #: lib/menu/selection_menu.py:476 lib/menu/global_menu.py:56 -#: lib/menu/global_menu.py:56 +#: lib/menu/global_menu.py:55 msgid "Select locale language" msgstr "" @@ -516,7 +496,7 @@ msgstr "" #: lib/menu/selection_menu.py:158 lib/menu/selection_menu.py:422 #: lib/menu/selection_menu.py:430 lib/user_interaction/global_menu.py:58 #: lib/menu/selection_menu.py:478 lib/menu/global_menu.py:58 -#: lib/menu/global_menu.py:58 +#: lib/menu/global_menu.py:57 msgid "Select locale encoding" msgstr "" @@ -524,7 +504,7 @@ msgstr "" #: lib/menu/selection_menu.py:161 lib/menu/selection_menu.py:425 #: lib/menu/selection_menu.py:433 lib/user_interaction/global_menu.py:61 #: lib/menu/selection_menu.py:481 lib/menu/global_menu.py:61 -#: lib/menu/global_menu.py:61 +#: lib/menu/global_menu.py:60 msgid "Select harddrives" msgstr "" @@ -532,7 +512,7 @@ msgstr "" #: lib/menu/selection_menu.py:165 lib/menu/selection_menu.py:429 #: lib/menu/selection_menu.py:437 lib/user_interaction/global_menu.py:65 #: lib/menu/selection_menu.py:485 lib/menu/global_menu.py:65 -#: lib/menu/global_menu.py:65 +#: lib/menu/global_menu.py:64 msgid "Select disk layout" msgstr "" @@ -540,7 +520,7 @@ msgstr "" #: lib/menu/selection_menu.py:173 lib/menu/selection_menu.py:437 #: lib/menu/selection_menu.py:445 lib/user_interaction/global_menu.py:73 #: lib/menu/selection_menu.py:493 lib/menu/global_menu.py:73 -#: lib/menu/global_menu.py:73 +#: lib/menu/global_menu.py:72 msgid "Set encryption password" msgstr "" @@ -548,7 +528,7 @@ msgstr "" #: lib/menu/selection_menu.py:179 lib/menu/selection_menu.py:443 #: lib/menu/selection_menu.py:451 lib/user_interaction/global_menu.py:79 #: lib/menu/selection_menu.py:499 lib/menu/global_menu.py:79 -#: lib/menu/global_menu.py:79 +#: lib/menu/global_menu.py:78 msgid "Use swap" msgstr "" @@ -556,7 +536,7 @@ msgstr "" #: lib/menu/selection_menu.py:184 lib/menu/selection_menu.py:448 #: lib/menu/selection_menu.py:456 lib/user_interaction/global_menu.py:84 #: lib/menu/selection_menu.py:504 lib/menu/global_menu.py:84 -#: lib/menu/global_menu.py:84 +#: lib/menu/global_menu.py:83 msgid "Select bootloader" msgstr "" @@ -564,7 +544,7 @@ msgstr "" #: lib/menu/selection_menu.py:190 lib/menu/selection_menu.py:454 #: lib/menu/selection_menu.py:462 lib/user_interaction/global_menu.py:95 #: lib/menu/selection_menu.py:515 lib/menu/global_menu.py:95 -#: lib/menu/global_menu.py:95 +#: lib/menu/global_menu.py:94 msgid "Set root password" msgstr "" @@ -572,7 +552,7 @@ msgstr "" #: lib/menu/selection_menu.py:195 lib/menu/selection_menu.py:459 #: lib/menu/selection_menu.py:467 lib/user_interaction/global_menu.py:100 #: lib/menu/selection_menu.py:520 lib/menu/global_menu.py:100 -#: lib/menu/global_menu.py:100 +#: lib/menu/global_menu.py:99 msgid "Specify superuser account" msgstr "" @@ -604,7 +584,7 @@ msgstr "" #: lib/menu/selection_menu.py:216 lib/menu/selection_menu.py:480 #: lib/menu/selection_menu.py:488 lib/user_interaction/global_menu.py:123 #: lib/menu/selection_menu.py:544 lib/menu/global_menu.py:123 -#: lib/menu/global_menu.py:123 +#: lib/menu/global_menu.py:126 msgid "Select kernels" msgstr "" @@ -612,7 +592,7 @@ msgstr "" #: lib/menu/selection_menu.py:221 lib/menu/selection_menu.py:485 #: lib/menu/selection_menu.py:493 lib/user_interaction/global_menu.py:128 #: lib/menu/selection_menu.py:549 lib/menu/global_menu.py:128 -#: lib/menu/global_menu.py:128 +#: lib/menu/global_menu.py:131 msgid "Additional packages to install" msgstr "" @@ -620,7 +600,7 @@ msgstr "" #: lib/menu/selection_menu.py:226 lib/menu/selection_menu.py:490 #: lib/menu/selection_menu.py:498 lib/user_interaction/global_menu.py:139 #: lib/menu/selection_menu.py:560 lib/menu/global_menu.py:139 -#: lib/menu/global_menu.py:139 +#: lib/menu/global_menu.py:142 msgid "Configure network" msgstr "" @@ -628,7 +608,7 @@ msgstr "" #: lib/menu/selection_menu.py:234 lib/menu/selection_menu.py:498 #: lib/menu/selection_menu.py:506 lib/user_interaction/global_menu.py:150 #: lib/menu/selection_menu.py:571 lib/menu/global_menu.py:150 -#: lib/menu/global_menu.py:150 +#: lib/menu/global_menu.py:153 msgid "Set automatic time sync (NTP)" msgstr "" @@ -636,7 +616,7 @@ msgstr "" #: lib/menu/selection_menu.py:315 lib/menu/selection_menu.py:527 #: lib/menu/selection_menu.py:535 lib/user_interaction/global_menu.py:187 #: lib/menu/selection_menu.py:608 lib/menu/global_menu.py:187 -#: lib/menu/global_menu.py:187 +#: lib/menu/global_menu.py:190 msgid "Install ({} config(s) missing)" msgstr "" @@ -644,7 +624,7 @@ msgstr "" #: lib/menu/selection_menu.py:378 lib/menu/selection_menu.py:591 #: lib/menu/selection_menu.py:599 lib/user_interaction/global_menu.py:249 #: lib/menu/selection_menu.py:669 lib/menu/global_menu.py:249 -#: lib/menu/global_menu.py:249 +#: lib/menu/global_menu.py:260 msgid "" "You decided to skip harddrive selection\n" "and will use whatever drive-setup is mounted at {} (experimental)\n" @@ -653,7 +633,7 @@ msgid "" msgstr "" #: lib/disk/filesystem.py:97 lib/disk/filesystem.py:98 -#: lib/disk/filesystem.py:98 +#: lib/disk/filesystem.py:96 msgid "Re-using partition instance: {}" msgstr "" @@ -661,7 +641,7 @@ msgstr "" #: lib/user_interaction.py:596 lib/user_interaction.py:595 #: lib/user_interaction/partitioning_conf.py:119 #: lib/user_interaction/partitioning_conf.py:120 -#: lib/user_interaction/partitioning_conf.py:120 +#: lib/user_interaction/partitioning_conf.py:128 msgid "Create a new partition" msgstr "" @@ -669,7 +649,7 @@ msgstr "" #: lib/user_interaction.py:598 lib/user_interaction.py:597 #: lib/user_interaction/partitioning_conf.py:121 #: lib/user_interaction/partitioning_conf.py:122 -#: lib/user_interaction/partitioning_conf.py:122 +#: lib/user_interaction/partitioning_conf.py:130 msgid "Delete a partition" msgstr "" @@ -677,7 +657,7 @@ msgstr "" #: lib/user_interaction.py:599 lib/user_interaction.py:598 #: lib/user_interaction/partitioning_conf.py:122 #: lib/user_interaction/partitioning_conf.py:123 -#: lib/user_interaction/partitioning_conf.py:123 +#: lib/user_interaction/partitioning_conf.py:131 msgid "Clear/Delete all partitions" msgstr "" @@ -685,7 +665,7 @@ msgstr "" #: lib/user_interaction.py:600 lib/user_interaction.py:599 #: lib/user_interaction/partitioning_conf.py:123 #: lib/user_interaction/partitioning_conf.py:124 -#: lib/user_interaction/partitioning_conf.py:124 +#: lib/user_interaction/partitioning_conf.py:132 msgid "Assign mount-point for a partition" msgstr "" @@ -693,7 +673,7 @@ msgstr "" #: lib/user_interaction.py:601 lib/user_interaction.py:600 #: lib/user_interaction/partitioning_conf.py:124 #: lib/user_interaction/partitioning_conf.py:125 -#: lib/user_interaction/partitioning_conf.py:125 +#: lib/user_interaction/partitioning_conf.py:133 msgid "Mark/Unmark a partition to be formatted (wipes data)" msgstr "" @@ -701,7 +681,7 @@ msgstr "" #: lib/user_interaction.py:602 lib/user_interaction.py:601 #: lib/user_interaction/partitioning_conf.py:125 #: lib/user_interaction/partitioning_conf.py:126 -#: lib/user_interaction/partitioning_conf.py:126 +#: lib/user_interaction/partitioning_conf.py:134 msgid "Mark/Unmark a partition as encrypted" msgstr "" @@ -709,7 +689,7 @@ msgstr "" #: lib/user_interaction.py:603 lib/user_interaction.py:602 #: lib/user_interaction/partitioning_conf.py:126 #: lib/user_interaction/partitioning_conf.py:127 -#: lib/user_interaction/partitioning_conf.py:127 +#: lib/user_interaction/partitioning_conf.py:136 msgid "Mark/Unmark a partition as bootable (automatic for /boot)" msgstr "" @@ -717,7 +697,7 @@ msgstr "" #: lib/user_interaction.py:604 lib/user_interaction.py:603 #: lib/user_interaction/partitioning_conf.py:127 #: lib/user_interaction/partitioning_conf.py:128 -#: lib/user_interaction/partitioning_conf.py:128 +#: lib/user_interaction/partitioning_conf.py:137 msgid "Set desired filesystem for a partition" msgstr "" @@ -725,33 +705,33 @@ msgstr "" #: lib/menu/selection_menu.py:241 lib/menu/selection_menu.py:270 #: lib/menu/selection_menu.py:507 lib/menu/selection_menu.py:515 #: lib/user_interaction/global_menu.py:167 lib/menu/selection_menu.py:588 -#: lib/menu/global_menu.py:167 lib/menu/global_menu.py:167 +#: lib/menu/global_menu.py:167 lib/menu/global_menu.py:170 msgid "Abort" msgstr "" #: lib/menu/selection_menu.py:183 lib/menu/selection_menu.py:187 #: lib/menu/selection_menu.py:451 lib/menu/selection_menu.py:459 #: lib/user_interaction/global_menu.py:89 lib/menu/selection_menu.py:509 -#: lib/menu/global_menu.py:89 lib/menu/global_menu.py:89 +#: lib/menu/global_menu.py:89 lib/menu/global_menu.py:88 msgid "Specify hostname" msgstr "" #: lib/menu/selection_menu.py:228 lib/menu/selection_menu.py:492 #: lib/menu/selection_menu.py:500 lib/user_interaction/global_menu.py:141 #: lib/menu/selection_menu.py:562 lib/menu/global_menu.py:141 -#: lib/menu/global_menu.py:141 +#: lib/menu/global_menu.py:195 msgid "Not configured, unavailable unless setup manually" msgstr "" #: lib/menu/selection_menu.py:231 lib/menu/selection_menu.py:495 #: lib/menu/selection_menu.py:503 lib/user_interaction/global_menu.py:145 #: lib/menu/selection_menu.py:566 lib/menu/global_menu.py:145 -#: lib/menu/global_menu.py:145 +#: lib/menu/global_menu.py:148 msgid "Select timezone" msgstr "" #: lib/menu/selection_menu.py:266 lib/menu/selection_menu.py:276 -#: lib/menu/selection_menu.py:312 lib/menu/selection_menu.py:312 +#: lib/menu/selection_menu.py:312 lib/menu/selection_menu.py:301 msgid "Set/Modify the below options" msgstr "" @@ -760,7 +740,7 @@ msgid "Install" msgstr "" #: lib/menu/menu.py:68 lib/menu/menu.py:75 lib/menu/menu.py:119 -#: lib/menu/menu.py:119 +#: lib/menu/menu.py:133 msgid "" "Use ESC to skip\n" "\n" @@ -770,24 +750,24 @@ msgstr "" #: lib/user_interaction.py:597 lib/user_interaction.py:596 #: lib/user_interaction/partitioning_conf.py:120 #: lib/user_interaction/partitioning_conf.py:121 -#: lib/user_interaction/partitioning_conf.py:121 +#: lib/user_interaction/partitioning_conf.py:129 msgid "Suggest partition layout" msgstr "" #: lib/user_interaction.py:101 lib/user_interaction/utils.py:43 -#: lib/user_interaction/utils.py:43 +#: lib/user_interaction/utils.py:40 msgid "Enter a password: " msgstr "" #: lib/disk/filesystem.py:117 lib/disk/filesystem.py:118 -#: lib/disk/filesystem.py:118 +#: lib/disk/filesystem.py:116 msgid "Enter a encryption password for {}" msgstr "" #: lib/menu/selection_menu.py:174 lib/menu/selection_menu.py:567 #: lib/menu/selection_menu.py:575 lib/user_interaction/global_menu.py:225 #: lib/menu/selection_menu.py:646 lib/menu/global_menu.py:225 -#: lib/menu/global_menu.py:225 +#: lib/menu/global_menu.py:236 msgid "Enter disk encryption password (leave blank for no encryption): " msgstr "" @@ -799,15 +779,15 @@ msgstr "" #: lib/menu/selection_menu.py:347 lib/menu/selection_menu.py:554 #: lib/menu/selection_menu.py:562 lib/user_interaction/global_menu.py:220 #: lib/menu/selection_menu.py:641 lib/menu/global_menu.py:220 -#: lib/menu/global_menu.py:220 +#: lib/menu/global_menu.py:231 msgid "Enter root password (leave blank to disable root): " msgstr "" #: lib/user_interaction.py:307 lib/user_interaction.py:324 #: lib/user_interaction/manage_users_conf.py:86 #: lib/user_interaction/manage_users_conf.py:121 -#: lib/user_interaction/manage_users_conf.py:86 -#: lib/user_interaction/manage_users_conf.py:121 +#: lib/user_interaction/manage_users_conf.py:83 +#: lib/user_interaction/manage_users_conf.py:118 msgid "Password for user \"{}\": " msgstr "" @@ -838,11 +818,11 @@ msgstr "" msgid "Enter a username to create an additional user (leave blank to skip): " msgstr "" -#: lib/menu/menu.py:116 lib/menu/menu.py:116 +#: lib/menu/menu.py:116 lib/menu/menu.py:130 msgid "Use ESC to skip\n" msgstr "" -#: lib/menu/list_manager.py:117 lib/menu/list_manager.py:117 +#: lib/menu/list_manager.py:117 lib/menu/list_manager.py:128 msgid "" "\n" " Choose an object from the list, and select one of the available actions for " @@ -850,11 +830,11 @@ msgid "" msgstr "" #: lib/menu/list_manager.py:130 lib/user_interaction/subvolume_config.py:90 -#: lib/menu/list_manager.py:130 lib/user_interaction/subvolume_config.py:90 +#: lib/menu/list_manager.py:141 lib/user_interaction/subvolume_config.py:93 msgid "Cancel" msgstr "" -#: lib/menu/list_manager.py:131 lib/menu/list_manager.py:131 +#: lib/menu/list_manager.py:131 lib/menu/list_manager.py:142 msgid "Confirm and exit" msgstr "" @@ -864,75 +844,75 @@ msgstr "" #: lib/user_interaction/subvolume_config.py:44 #: lib/user_interaction/subvolume_config.py:78 #: lib/user_interaction/subvolume_config.py:82 -#: lib/user_interaction/subvolume_config.py:85 lib/menu/list_manager.py:135 -#: lib/menu/list_manager.py:228 lib/menu/list_manager.py:251 -#: lib/menu/list_manager.py:276 lib/user_interaction/subvolume_config.py:12 -#: lib/user_interaction/subvolume_config.py:44 -#: lib/user_interaction/subvolume_config.py:78 -#: lib/user_interaction/subvolume_config.py:82 +#: lib/user_interaction/subvolume_config.py:85 lib/menu/list_manager.py:146 +#: lib/menu/list_manager.py:249 lib/menu/list_manager.py:272 +#: lib/user_interaction/subvolume_config.py:15 +#: lib/user_interaction/subvolume_config.py:47 +#: lib/user_interaction/subvolume_config.py:81 #: lib/user_interaction/subvolume_config.py:85 +#: lib/user_interaction/subvolume_config.py:88 msgid "Add" msgstr "" #: lib/menu/list_manager.py:135 lib/menu/list_manager.py:231 #: lib/menu/list_manager.py:255 lib/user_interaction/subvolume_config.py:78 -#: lib/menu/list_manager.py:135 lib/menu/list_manager.py:231 -#: lib/menu/list_manager.py:255 lib/user_interaction/subvolume_config.py:78 +#: lib/menu/list_manager.py:146 lib/menu/list_manager.py:252 +#: lib/menu/list_manager.py:276 lib/user_interaction/subvolume_config.py:81 msgid "Copy" msgstr "" #: lib/menu/list_manager.py:135 lib/menu/list_manager.py:237 #: lib/menu/list_manager.py:261 lib/user_interaction/subvolume_config.py:82 -#: lib/user_interaction/subvolume_config.py:85 lib/menu/list_manager.py:135 -#: lib/menu/list_manager.py:237 lib/menu/list_manager.py:261 -#: lib/user_interaction/subvolume_config.py:82 +#: lib/user_interaction/subvolume_config.py:85 lib/menu/list_manager.py:146 +#: lib/menu/list_manager.py:258 lib/menu/list_manager.py:282 #: lib/user_interaction/subvolume_config.py:85 +#: lib/user_interaction/subvolume_config.py:88 msgid "Edit" msgstr "" #: lib/menu/list_manager.py:135 lib/menu/list_manager.py:242 #: lib/menu/list_manager.py:264 lib/user_interaction/subvolume_config.py:40 -#: lib/menu/list_manager.py:135 lib/menu/list_manager.py:242 -#: lib/menu/list_manager.py:264 lib/user_interaction/subvolume_config.py:40 +#: lib/menu/list_manager.py:146 lib/menu/list_manager.py:263 +#: lib/menu/list_manager.py:285 lib/user_interaction/subvolume_config.py:44 msgid "Delete" msgstr "" -#: lib/menu/list_manager.py:185 lib/menu/list_manager.py:185 +#: lib/menu/list_manager.py:185 lib/menu/list_manager.py:206 msgid "Select an action for < {} >" msgstr "" -#: lib/menu/list_manager.py:229 lib/menu/list_manager.py:229 +#: lib/menu/list_manager.py:229 lib/menu/list_manager.py:250 msgid "Add :" msgstr "" -#: lib/menu/list_manager.py:233 lib/menu/list_manager.py:233 +#: lib/menu/list_manager.py:233 lib/menu/list_manager.py:254 msgid "Copy to :" msgstr "" -#: lib/menu/list_manager.py:240 lib/menu/list_manager.py:240 +#: lib/menu/list_manager.py:240 lib/menu/list_manager.py:261 msgid "Edite :" msgstr "" -#: lib/menu/list_manager.py:252 lib/menu/list_manager.py:252 +#: lib/menu/list_manager.py:252 lib/menu/list_manager.py:273 msgid "Key :" msgstr "" -#: lib/menu/list_manager.py:253 lib/menu/list_manager.py:253 +#: lib/menu/list_manager.py:253 lib/menu/list_manager.py:274 msgid "Value :" msgstr "" -#: lib/menu/list_manager.py:257 lib/menu/list_manager.py:257 +#: lib/menu/list_manager.py:257 lib/menu/list_manager.py:278 msgid "Copy to new key:" msgstr "" -#: lib/menu/list_manager.py:262 lib/menu/list_manager.py:262 +#: lib/menu/list_manager.py:262 lib/menu/list_manager.py:283 #, python-brace-format msgid "Edit {origkey} :" msgstr "" #: lib/models/network_configuration.py:53 #: lib/models/network_configuration.py:74 -#: lib/models/network_configuration.py:74 +#: lib/models/network_configuration.py:167 msgid "Unknown nic type: {}. Possible values are {}" msgstr "" @@ -979,19 +959,19 @@ msgid "Delete User" msgstr "" #: lib/user_interaction/manage_users_conf.py:99 -#: lib/user_interaction/manage_users_conf.py:99 +#: lib/user_interaction/manage_users_conf.py:96 msgid "" "\n" "Define a new user\n" msgstr "" #: lib/user_interaction/manage_users_conf.py:100 -#: lib/user_interaction/manage_users_conf.py:100 +#: lib/user_interaction/manage_users_conf.py:97 msgid "User Name : " msgstr "" #: lib/user_interaction/manage_users_conf.py:115 -#: lib/user_interaction/manage_users_conf.py:115 +#: lib/user_interaction/manage_users_conf.py:112 msgid "Should {} be a superuser (sudoer)?" msgstr "" @@ -1000,19 +980,19 @@ msgid "Define users with sudo privilege: " msgstr "" #: lib/user_interaction/network_conf.py:24 -#: lib/user_interaction/network_conf.py:24 +#: lib/user_interaction/network_conf.py:124 msgid "No network configuration" msgstr "" #: lib/user_interaction/partitioning_conf.py:128 #: lib/user_interaction/partitioning_conf.py:129 -#: lib/user_interaction/partitioning_conf.py:129 +#: lib/user_interaction/partitioning_conf.py:138 msgid "Set desired subvolumes on a btrfs partition" msgstr "" #: lib/user_interaction/partitioning_conf.py:296 #: lib/user_interaction/partitioning_conf.py:303 -#: lib/user_interaction/partitioning_conf.py:303 +#: lib/user_interaction/partitioning_conf.py:336 msgid "" "{}\n" "\n" @@ -1021,7 +1001,7 @@ msgstr "" #: lib/user_interaction/partitioning_conf.py:305 #: lib/user_interaction/partitioning_conf.py:312 -#: lib/user_interaction/partitioning_conf.py:312 +#: lib/user_interaction/partitioning_conf.py:346 msgid "Manage btrfs subvolumes for current partition" msgstr "" @@ -1058,108 +1038,226 @@ msgstr "" msgid "Not a valid directory: {}" msgstr "" -#: lib/user_interaction/utils.py:32 lib/user_interaction/utils.py:32 +#: lib/user_interaction/utils.py:32 msgid "The password you are using seems to be weak," msgstr "" -#: lib/user_interaction/utils.py:33 lib/user_interaction/utils.py:33 +#: lib/user_interaction/utils.py:33 msgid "are you sure you want to use it?" msgstr "" #: lib/user_interaction/global_menu.py:134 lib/menu/selection_menu.py:555 -#: lib/menu/global_menu.py:134 lib/menu/global_menu.py:134 +#: lib/menu/global_menu.py:134 lib/menu/global_menu.py:137 msgid "Additional repositories to enable" msgstr "" #: lib/user_interaction/global_menu.py:155 lib/menu/selection_menu.py:576 -#: lib/menu/global_menu.py:155 lib/menu/global_menu.py:155 +#: lib/menu/global_menu.py:155 lib/menu/global_menu.py:160 msgid "Save configuration" msgstr "" #: lib/user_interaction/global_menu.py:192 lib/menu/selection_menu.py:613 -#: lib/menu/global_menu.py:192 lib/menu/global_menu.py:192 +#: lib/menu/global_menu.py:192 lib/menu/global_menu.py:205 msgid "Missing configurations:\n" msgstr "" #: lib/user_interaction/global_menu.py:210 lib/menu/selection_menu.py:631 -#: lib/menu/global_menu.py:210 lib/menu/global_menu.py:210 +#: lib/menu/global_menu.py:210 lib/menu/global_menu.py:221 msgid "Either root-password or at least 1 superuser must be specified" msgstr "" #: lib/user_interaction/global_menu.py:275 lib/menu/selection_menu.py:695 -#: lib/menu/global_menu.py:275 lib/menu/global_menu.py:275 +#: lib/menu/global_menu.py:275 lib/menu/global_menu.py:287 msgid "Manage superuser accounts: " msgstr "" #: lib/user_interaction/global_menu.py:279 lib/menu/selection_menu.py:699 -#: lib/menu/global_menu.py:279 lib/menu/global_menu.py:279 +#: lib/menu/global_menu.py:279 lib/menu/global_menu.py:291 msgid "Manage ordinary user accounts: " msgstr "" #: lib/user_interaction/subvolume_config.py:17 -#: lib/user_interaction/subvolume_config.py:17 +#: lib/user_interaction/subvolume_config.py:20 msgid " Subvolume :{:16}" msgstr "" #: lib/user_interaction/subvolume_config.py:19 #: lib/user_interaction/subvolume_config.py:22 -#: lib/user_interaction/subvolume_config.py:19 #: lib/user_interaction/subvolume_config.py:22 +#: lib/user_interaction/subvolume_config.py:25 msgid " mounted at {:16}" msgstr "" #: lib/user_interaction/subvolume_config.py:26 -#: lib/user_interaction/subvolume_config.py:26 +#: lib/user_interaction/subvolume_config.py:29 msgid " with option {}" msgstr "" #: lib/user_interaction/subvolume_config.py:46 -#: lib/user_interaction/subvolume_config.py:46 +#: lib/user_interaction/subvolume_config.py:49 msgid "" "\n" " Fill the desired values for a new subvolume \n" msgstr "" #: lib/user_interaction/subvolume_config.py:77 -#: lib/user_interaction/subvolume_config.py:77 +#: lib/user_interaction/subvolume_config.py:80 msgid "Subvolume name " msgstr "" #: lib/user_interaction/subvolume_config.py:81 -#: lib/user_interaction/subvolume_config.py:81 +#: lib/user_interaction/subvolume_config.py:84 msgid "Subvolume mountpoint" msgstr "" #: lib/user_interaction/subvolume_config.py:84 -#: lib/user_interaction/subvolume_config.py:84 +#: lib/user_interaction/subvolume_config.py:87 msgid "Subvolume options" msgstr "" #: lib/user_interaction/subvolume_config.py:87 -#: lib/user_interaction/subvolume_config.py:87 +#: lib/user_interaction/subvolume_config.py:90 msgid "Save" msgstr "" #: lib/user_interaction/subvolume_config.py:121 -#: lib/user_interaction/subvolume_config.py:121 +#: lib/user_interaction/subvolume_config.py:124 msgid "Subvolume name :" msgstr "" #: lib/user_interaction/subvolume_config.py:124 -#: lib/user_interaction/subvolume_config.py:124 +#: lib/user_interaction/subvolume_config.py:127 msgid "Select a mount point :" msgstr "" #: lib/user_interaction/subvolume_config.py:128 -#: lib/user_interaction/subvolume_config.py:128 +#: lib/user_interaction/subvolume_config.py:131 msgid "Select the desired subvolume options " msgstr "" #: lib/user_interaction/manage_users_conf.py:161 -#: lib/user_interaction/manage_users_conf.py:161 +#: lib/user_interaction/manage_users_conf.py:158 msgid "Define users with sudo privilege, by username: " msgstr "" -#: lib/installer.py:152 lib/installer.py:152 +#: lib/installer.py:152 lib/installer.py:154 msgid "[!] A log file has been created here: {}" msgstr "" + +#: lib/disk/user_guides.py:30 +msgid "Would you like to use BTRFS subvolumes with a default structure?" +msgstr "" + +#: lib/disk/user_guides.py:34 lib/disk/user_guides.py:174 +msgid "Would you like to use BTRFS compression?" +msgstr "" + +#: lib/disk/user_guides.py:91 +msgid "Would you like to create a separate partition for /home?" +msgstr "" + +#: lib/disk/user_guides.py:161 +msgid "" +"The selected drives do not have the minimum capacity required for an " +"automatic suggestion\n" +msgstr "" + +#: lib/disk/user_guides.py:162 +msgid "Minimum capacity for /home partition: {}GB\n" +msgstr "" + +#: lib/disk/user_guides.py:163 +msgid "Minimum capacity for Arch Linux partition: {}GB" +msgstr "" + +#: lib/disk/user_guides.py:164 +msgid "Continue" +msgstr "" + +#: lib/menu/menu.py:20 +msgid "yes" +msgstr "" + +#: lib/menu/menu.py:24 +msgid "no" +msgstr "" + +#: lib/menu/selection_menu.py:137 +msgid "set: {}" +msgstr "" + +#: lib/models/network_configuration.py:118 +msgid "Manual configuration setting must be a list" +msgstr "" + +#: lib/models/network_configuration.py:127 +msgid "No iface specified for manual configuration" +msgstr "" + +#: lib/models/network_configuration.py:137 +msgid "Manual nic configuration with no auto DHCP requires an IP address" +msgstr "" + +#: lib/user_interaction/network_conf.py:37 +msgid "Add interface" +msgstr "" + +#: lib/user_interaction/network_conf.py:38 +msgid "Edit interface" +msgstr "" + +#: lib/user_interaction/network_conf.py:39 +msgid "Delete interface" +msgstr "" + +#: lib/user_interaction/network_conf.py:69 +msgid "Select interface to add" +msgstr "" + +#: lib/user_interaction/network_conf.py:127 +msgid "Manual configuration" +msgstr "" + +#: lib/user_interaction/partitioning_conf.py:135 +msgid "Mark/Unmark a partition as compressed (btrfs only)" +msgstr "" + +#: lib/user_interaction/utils.py:31 +msgid "" +"The password you are using seems to be weak, are you sure you want to use it?" +msgstr "" + +#: profiles/desktop.py:7 +msgid "" +"Provides a selection of desktop environments and tiling window managers, e.g. gnome, kde, sway" +msgstr "" + +#: profiles/desktop.py:49 +msgid "Select your desired desktop environment" +msgstr "" + +#: profiles/minimal.py:5 +msgid "A very basic installation that allows you to customize Arch Linux as you see fit." +msgstr "" + +#: profiles/server.py:9 +msgid "" +"Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb" +msgstr "" + +#: profiles/server.py:30 +msgid "Choose which servers to install, if none then a minimal installation wil be done" +msgstr "" + +#: profiles/xorg.py:9 +msgid "Installs a minimal system as well as xorg and graphics drivers." +msgstr "" + +#: examples/guided.py:303 +msgid "Press Enter to continue." +msgstr "" + +#: examples/guided.py:260 +msgid "" +"Would you like to chroot into the newly created installation and perform post-installation configuration?" +msgstr "" diff --git a/archinstall/locales/ru/LC_MESSAGES/base.mo b/archinstall/locales/ru/LC_MESSAGES/base.mo index d2e49b43..53dcba03 100644 Binary files a/archinstall/locales/ru/LC_MESSAGES/base.mo and b/archinstall/locales/ru/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/ru/LC_MESSAGES/base.po b/archinstall/locales/ru/LC_MESSAGES/base.po index d5decb42..e76f2b31 100644 --- a/archinstall/locales/ru/LC_MESSAGES/base.po +++ b/archinstall/locales/ru/LC_MESSAGES/base.po @@ -17,7 +17,7 @@ msgstr "" msgid "[!] A log file has been created here: {} {}" msgstr "[!] Здесь был создан файл журнала: {} {}" -#: lib/installer.py:145 lib/installer.py:153 +#: lib/installer.py:145 lib/installer.py:153 lib/installer.py:155 msgid "" " Please submit this issue (and file) to https://github.com/archlinux/" "archinstall/issues" @@ -26,16 +26,18 @@ msgstr "" "archlinux/archinstall/issues" #: lib/user_interaction.py:83 lib/user_interaction/utils.py:86 +#: lib/user_interaction/utils.py:83 msgid "Do you really want to abort?" msgstr "Вы действительно хотите прекратить?" #: lib/user_interaction.py:101 lib/user_interaction.py:104 -#: lib/user_interaction/utils.py:53 +#: lib/user_interaction/utils.py:53 lib/user_interaction/utils.py:50 msgid "And one more time for verification: " msgstr "И еще раз для проверки: " #: lib/user_interaction.py:272 lib/user_interaction.py:275 #: lib/user_interaction/system_conf.py:142 +#: lib/user_interaction/system_conf.py:141 msgid "Would you like to use swap on zram?" msgstr "Вы хотите использовать подкачку на zram?" @@ -51,6 +53,7 @@ msgstr "" #: lib/user_interaction.py:310 lib/user_interaction.py:313 #: lib/user_interaction/manage_users_conf.py:167 +#: lib/user_interaction/manage_users_conf.py:164 msgid "Any additional users to install (leave blank for no users): " msgstr "" "Любые дополнительные пользователи для установки (оставьте пустым, если " @@ -68,12 +71,14 @@ msgstr "Выберите часовой пояс" #: lib/user_interaction.py:354 lib/user_interaction.py:357 #: lib/user_interaction.py:360 lib/user_interaction.py:359 #: lib/user_interaction/system_conf.py:115 +#: lib/user_interaction/system_conf.py:114 msgid "Would you like to use GRUB as a bootloader instead of systemd-boot?" msgstr "Вы хотите использовать GRUB в качестве загрузчика вместо systemd-boot?" #: lib/user_interaction.py:364 lib/user_interaction.py:367 #: lib/user_interaction.py:370 lib/user_interaction.py:369 #: lib/user_interaction/system_conf.py:125 +#: lib/user_interaction/system_conf.py:124 msgid "Choose a bootloader" msgstr "Выберите загрузчик" @@ -115,12 +120,14 @@ msgstr "" #: lib/user_interaction.py:419 lib/user_interaction.py:422 #: lib/user_interaction.py:425 lib/user_interaction.py:424 #: lib/user_interaction/network_conf.py:25 +#: lib/user_interaction/network_conf.py:125 msgid "Copy ISO network configuration to installation" msgstr "Копировать сетевую конфигурацию ISO в установку" #: lib/user_interaction.py:420 lib/user_interaction.py:423 #: lib/user_interaction.py:426 lib/user_interaction.py:425 #: lib/user_interaction/network_conf.py:26 +#: lib/user_interaction/network_conf.py:126 msgid "" "Use NetworkManager (necessary to configure internet graphically in GNOME and " "KDE)" @@ -131,12 +138,14 @@ msgstr "" #: lib/user_interaction.py:428 lib/user_interaction.py:431 #: lib/user_interaction.py:434 lib/user_interaction.py:433 #: lib/user_interaction/network_conf.py:43 +#: lib/user_interaction/network_conf.py:139 msgid "Select one network interface to configure" msgstr "Выберите один сетевой интерфейс для настройки" #: lib/user_interaction.py:441 lib/user_interaction.py:444 #: lib/user_interaction.py:447 lib/user_interaction.py:446 #: lib/user_interaction/network_conf.py:70 +#: lib/user_interaction/network_conf.py:77 msgid "" "Select which mode to configure for \"{}\" or skip to use default mode \"{}\"" msgstr "" @@ -146,12 +155,14 @@ msgstr "" #: lib/user_interaction.py:446 lib/user_interaction.py:449 #: lib/user_interaction.py:452 lib/user_interaction.py:451 #: lib/user_interaction/network_conf.py:75 +#: lib/user_interaction/network_conf.py:82 msgid "Enter the IP and subnet for {} (example: 192.168.0.5/24): " msgstr "Введите IP-адрес и подсеть для {} (пример: 192.168.0.5/24): " #: lib/user_interaction.py:461 lib/user_interaction.py:464 #: lib/user_interaction.py:467 lib/user_interaction.py:466 #: lib/user_interaction/network_conf.py:86 +#: lib/user_interaction/network_conf.py:93 msgid "Enter your gateway (router) IP address or leave blank for none: " msgstr "" "Введите IP-адрес вашего шлюза (маршрутизатора) или оставьте пустым, если его " @@ -160,6 +171,7 @@ msgstr "" #: lib/user_interaction.py:476 lib/user_interaction.py:479 #: lib/user_interaction.py:482 lib/user_interaction.py:481 #: lib/user_interaction/network_conf.py:102 +#: lib/user_interaction/network_conf.py:108 msgid "Enter your DNS servers (space separated, blank for none): " msgstr "Введите ваши DNS-серверы (через пробел, пустой - нет): " @@ -172,8 +184,8 @@ msgstr "" #: lib/user_interaction.py:556 lib/user_interaction.py:559 #: lib/user_interaction.py:562 lib/user_interaction.py:561 -#: lib/user_interaction/partitioning_conf.py:64 #: lib/user_interaction/partitioning_conf.py:63 +#: lib/user_interaction/partitioning_conf.py:64 msgid "Current partition layout" msgstr "Текущая разметка разделов" @@ -181,6 +193,7 @@ msgstr "Текущая разметка разделов" #: lib/user_interaction.py:617 lib/user_interaction.py:620 #: lib/user_interaction.py:619 lib/user_interaction/partitioning_conf.py:145 #: lib/user_interaction/partitioning_conf.py:146 +#: lib/user_interaction/partitioning_conf.py:163 msgid "" "Select what to do with\n" "{}" @@ -197,6 +210,8 @@ msgstr "" #: lib/user_interaction/partitioning_conf.py:255 #: lib/user_interaction/partitioning_conf.py:165 #: lib/user_interaction/partitioning_conf.py:260 +#: lib/user_interaction/partitioning_conf.py:182 +#: lib/user_interaction/partitioning_conf.py:293 msgid "Enter a desired filesystem type for the partition" msgstr "Введите желаемый тип файловой системы для раздела" @@ -204,6 +219,7 @@ msgstr "Введите желаемый тип файловой системы #: lib/user_interaction.py:636 lib/user_interaction.py:639 #: lib/user_interaction.py:638 lib/user_interaction/partitioning_conf.py:164 #: lib/user_interaction/partitioning_conf.py:167 +#: lib/user_interaction/partitioning_conf.py:187 msgid "Enter the start sector (percentage or block number, default: {}): " msgstr "Введите начальный сектор (процент или номер блока, по умолчанию: {}): " @@ -211,6 +227,7 @@ msgstr "Введите начальный сектор (процент или н #: lib/user_interaction.py:645 lib/user_interaction.py:648 #: lib/user_interaction.py:647 lib/user_interaction/partitioning_conf.py:174 #: lib/user_interaction/partitioning_conf.py:177 +#: lib/user_interaction/partitioning_conf.py:197 msgid "" "Enter the end sector of the partition (percentage or block number, ex: {}): " msgstr "" @@ -220,6 +237,7 @@ msgstr "" #: lib/user_interaction.py:671 lib/user_interaction.py:674 #: lib/user_interaction.py:673 lib/user_interaction/partitioning_conf.py:203 #: lib/user_interaction/partitioning_conf.py:208 +#: lib/user_interaction/partitioning_conf.py:228 msgid "{} contains queued partitions, this will remove those, are you sure?" msgstr "{} содержит разделы в очереди, это удалит их, вы уверены?" @@ -227,6 +245,7 @@ msgstr "{} содержит разделы в очереди, это удали #: lib/user_interaction.py:684 lib/user_interaction.py:687 #: lib/user_interaction.py:686 lib/user_interaction/partitioning_conf.py:216 #: lib/user_interaction/partitioning_conf.py:221 +#: lib/user_interaction/partitioning_conf.py:242 msgid "" "{}\n" "\n" @@ -240,6 +259,7 @@ msgstr "" #: lib/user_interaction.py:692 lib/user_interaction.py:695 #: lib/user_interaction.py:694 lib/user_interaction/partitioning_conf.py:226 #: lib/user_interaction/partitioning_conf.py:231 +#: lib/user_interaction/partitioning_conf.py:264 msgid "" "{}\n" "\n" @@ -253,6 +273,7 @@ msgstr "" #: lib/user_interaction.py:696 lib/user_interaction.py:699 #: lib/user_interaction.py:698 lib/user_interaction/partitioning_conf.py:231 #: lib/user_interaction/partitioning_conf.py:236 +#: lib/user_interaction/partitioning_conf.py:269 msgid "" " * Partition mount-points are relative to inside the installation, the boot " "would be /boot as an example." @@ -264,6 +285,7 @@ msgstr "" #: lib/user_interaction.py:697 lib/user_interaction.py:700 #: lib/user_interaction.py:699 lib/user_interaction/partitioning_conf.py:233 #: lib/user_interaction/partitioning_conf.py:238 +#: lib/user_interaction/partitioning_conf.py:271 msgid "Select where to mount partition (leave blank to remove mountpoint): " msgstr "" "Выберите куда монтировать раздел (оставьте пустым, чтобы удалить точку " @@ -273,6 +295,7 @@ msgstr "" #: lib/user_interaction.py:708 lib/user_interaction.py:711 #: lib/user_interaction.py:710 lib/user_interaction/partitioning_conf.py:244 #: lib/user_interaction/partitioning_conf.py:249 +#: lib/user_interaction/partitioning_conf.py:282 msgid "" "{}\n" "\n" @@ -286,6 +309,7 @@ msgstr "" #: lib/user_interaction.py:727 lib/user_interaction.py:730 #: lib/user_interaction.py:729 lib/user_interaction/partitioning_conf.py:265 #: lib/user_interaction/partitioning_conf.py:270 +#: lib/user_interaction/partitioning_conf.py:302 msgid "" "{}\n" "\n" @@ -299,6 +323,8 @@ msgstr "" #: lib/user_interaction.py:735 lib/user_interaction.py:738 #: lib/user_interaction.py:737 lib/user_interaction/partitioning_conf.py:274 #: lib/user_interaction/partitioning_conf.py:279 +#: lib/user_interaction/partitioning_conf.py:250 +#: lib/user_interaction/partitioning_conf.py:311 msgid "" "{}\n" "\n" @@ -312,6 +338,7 @@ msgstr "" #: lib/user_interaction.py:742 lib/user_interaction.py:745 #: lib/user_interaction.py:744 lib/user_interaction/partitioning_conf.py:282 #: lib/user_interaction/partitioning_conf.py:287 +#: lib/user_interaction/partitioning_conf.py:319 msgid "" "{}\n" "\n" @@ -325,6 +352,7 @@ msgstr "" #: lib/user_interaction.py:749 lib/user_interaction.py:752 #: lib/user_interaction.py:751 lib/user_interaction/partitioning_conf.py:289 #: lib/user_interaction/partitioning_conf.py:294 +#: lib/user_interaction/partitioning_conf.py:326 msgid "Enter a desired filesystem type for the partition: " msgstr "Введите желаемый тип файловой системы для раздела: " @@ -336,6 +364,7 @@ msgstr "Введите желаемый тип файловой системы #: lib/menu/selection_menu.py:17 lib/user_interaction/general_conf.py:105 #: lib/user_interaction/global_menu.py:43 lib/menu/selection_menu.py:53 #: lib/menu/selection_menu.py:463 lib/menu/global_menu.py:43 +#: lib/menu/selection_menu.py:20 msgid "Select Archinstall language" msgstr "Выберите язык Archinstall" @@ -359,7 +388,7 @@ msgstr "" #: lib/user_interaction.py:769 lib/user_interaction.py:770 #: lib/user_interaction.py:777 lib/user_interaction.py:780 #: lib/user_interaction.py:783 lib/user_interaction.py:782 -#: lib/user_interaction/disk_conf.py:45 +#: lib/user_interaction/disk_conf.py:45 lib/user_interaction/disk_conf.py:44 msgid "Select what you wish to do with the selected block devices" msgstr "Выберите, что вы хотите сделать с выбранными блочными устройствами" @@ -433,6 +462,7 @@ msgstr "" #: lib/user_interaction.py:925 lib/user_interaction.py:928 #: lib/user_interaction.py:931 lib/user_interaction.py:930 #: lib/user_interaction/system_conf.py:92 +#: lib/user_interaction/system_conf.py:91 msgid "" "\n" "\n" @@ -447,6 +477,7 @@ msgstr "" #: lib/user_interaction.py:929 lib/user_interaction.py:932 #: lib/user_interaction.py:935 lib/user_interaction.py:934 #: lib/user_interaction/system_conf.py:96 +#: lib/user_interaction/system_conf.py:95 msgid "All open-source (default)" msgstr "Все с открытым исходным кодом (по умолчанию)" @@ -487,11 +518,12 @@ msgid "Select one or more of the options below: " msgstr "Выберите один или несколько из приведенных ниже вариантов: " #: lib/disk/filesystem.py:86 lib/disk/filesystem.py:87 +#: lib/disk/filesystem.py:85 msgid "Adding partition...." msgstr "Добавление раздела...." #: lib/disk/filesystem.py:139 lib/disk/filesystem.py:141 -#: lib/disk/filesystem.py:142 +#: lib/disk/filesystem.py:142 lib/disk/filesystem.py:140 msgid "" "You need to enter a valid fs-type in order to continue. See `man parted` for " "valid fs-type's." @@ -511,6 +543,7 @@ msgstr "Ошибка: Не удалось декодировать резуль #: lib/menu/selection_menu.py:148 lib/menu/selection_menu.py:412 #: lib/menu/selection_menu.py:420 lib/user_interaction/global_menu.py:48 #: lib/menu/selection_menu.py:468 lib/menu/global_menu.py:48 +#: lib/menu/global_menu.py:47 msgid "Select keyboard layout" msgstr "Выберите раскладку клавиатуры" @@ -518,6 +551,7 @@ msgstr "Выберите раскладку клавиатуры" #: lib/menu/selection_menu.py:151 lib/menu/selection_menu.py:415 #: lib/menu/selection_menu.py:423 lib/user_interaction/global_menu.py:51 #: lib/menu/selection_menu.py:471 lib/menu/global_menu.py:51 +#: lib/menu/global_menu.py:50 msgid "Select mirror region" msgstr "Выберите регион зеркала" @@ -525,6 +559,7 @@ msgstr "Выберите регион зеркала" #: lib/menu/selection_menu.py:156 lib/menu/selection_menu.py:420 #: lib/menu/selection_menu.py:428 lib/user_interaction/global_menu.py:56 #: lib/menu/selection_menu.py:476 lib/menu/global_menu.py:56 +#: lib/menu/global_menu.py:55 msgid "Select locale language" msgstr "Выберите язык локали" @@ -532,6 +567,7 @@ msgstr "Выберите язык локали" #: lib/menu/selection_menu.py:158 lib/menu/selection_menu.py:422 #: lib/menu/selection_menu.py:430 lib/user_interaction/global_menu.py:58 #: lib/menu/selection_menu.py:478 lib/menu/global_menu.py:58 +#: lib/menu/global_menu.py:57 msgid "Select locale encoding" msgstr "Выберите кодировку локали" @@ -539,6 +575,7 @@ msgstr "Выберите кодировку локали" #: lib/menu/selection_menu.py:161 lib/menu/selection_menu.py:425 #: lib/menu/selection_menu.py:433 lib/user_interaction/global_menu.py:61 #: lib/menu/selection_menu.py:481 lib/menu/global_menu.py:61 +#: lib/menu/global_menu.py:60 msgid "Select harddrives" msgstr "Выберите жесткие диски" @@ -546,6 +583,7 @@ msgstr "Выберите жесткие диски" #: lib/menu/selection_menu.py:165 lib/menu/selection_menu.py:429 #: lib/menu/selection_menu.py:437 lib/user_interaction/global_menu.py:65 #: lib/menu/selection_menu.py:485 lib/menu/global_menu.py:65 +#: lib/menu/global_menu.py:64 msgid "Select disk layout" msgstr "Выберите разметку диска" @@ -553,6 +591,7 @@ msgstr "Выберите разметку диска" #: lib/menu/selection_menu.py:173 lib/menu/selection_menu.py:437 #: lib/menu/selection_menu.py:445 lib/user_interaction/global_menu.py:73 #: lib/menu/selection_menu.py:493 lib/menu/global_menu.py:73 +#: lib/menu/global_menu.py:72 msgid "Set encryption password" msgstr "Установите пароль шифрования" @@ -560,6 +599,7 @@ msgstr "Установите пароль шифрования" #: lib/menu/selection_menu.py:179 lib/menu/selection_menu.py:443 #: lib/menu/selection_menu.py:451 lib/user_interaction/global_menu.py:79 #: lib/menu/selection_menu.py:499 lib/menu/global_menu.py:79 +#: lib/menu/global_menu.py:78 msgid "Use swap" msgstr "Использовать подкачку" @@ -567,6 +607,7 @@ msgstr "Использовать подкачку" #: lib/menu/selection_menu.py:184 lib/menu/selection_menu.py:448 #: lib/menu/selection_menu.py:456 lib/user_interaction/global_menu.py:84 #: lib/menu/selection_menu.py:504 lib/menu/global_menu.py:84 +#: lib/menu/global_menu.py:83 msgid "Select bootloader" msgstr "Выберите загрузчик" @@ -574,6 +615,7 @@ msgstr "Выберите загрузчик" #: lib/menu/selection_menu.py:190 lib/menu/selection_menu.py:454 #: lib/menu/selection_menu.py:462 lib/user_interaction/global_menu.py:95 #: lib/menu/selection_menu.py:515 lib/menu/global_menu.py:95 +#: lib/menu/global_menu.py:94 msgid "Set root password" msgstr "Установите пароль root" @@ -581,6 +623,7 @@ msgstr "Установите пароль root" #: lib/menu/selection_menu.py:195 lib/menu/selection_menu.py:459 #: lib/menu/selection_menu.py:467 lib/user_interaction/global_menu.py:100 #: lib/menu/selection_menu.py:520 lib/menu/global_menu.py:100 +#: lib/menu/global_menu.py:99 msgid "Specify superuser account" msgstr "Укажите учетную запись суперпользователя" @@ -609,6 +652,7 @@ msgstr "Выберите аудиоустройство" #: lib/menu/selection_menu.py:216 lib/menu/selection_menu.py:480 #: lib/menu/selection_menu.py:488 lib/user_interaction/global_menu.py:123 #: lib/menu/selection_menu.py:544 lib/menu/global_menu.py:123 +#: lib/menu/global_menu.py:126 msgid "Select kernels" msgstr "Выберите ядра" @@ -616,6 +660,7 @@ msgstr "Выберите ядра" #: lib/menu/selection_menu.py:221 lib/menu/selection_menu.py:485 #: lib/menu/selection_menu.py:493 lib/user_interaction/global_menu.py:128 #: lib/menu/selection_menu.py:549 lib/menu/global_menu.py:128 +#: lib/menu/global_menu.py:131 msgid "Additional packages to install" msgstr "Дополнительные пакеты для установки" @@ -623,20 +668,23 @@ msgstr "Дополнительные пакеты для установки" #: lib/menu/selection_menu.py:226 lib/menu/selection_menu.py:490 #: lib/menu/selection_menu.py:498 lib/user_interaction/global_menu.py:139 #: lib/menu/selection_menu.py:560 lib/menu/global_menu.py:139 +#: lib/menu/global_menu.py:142 msgid "Configure network" -msgstr "Настроить сеть" +msgstr "Настройте сеть" #: lib/menu/selection_menu.py:232 lib/menu/selection_menu.py:230 #: lib/menu/selection_menu.py:234 lib/menu/selection_menu.py:498 #: lib/menu/selection_menu.py:506 lib/user_interaction/global_menu.py:150 #: lib/menu/selection_menu.py:571 lib/menu/global_menu.py:150 +#: lib/menu/global_menu.py:153 msgid "Set automatic time sync (NTP)" -msgstr "Установите автоматическую синхронизацию времени (NTP)" +msgstr "Установить автоматическую синхронизацию времени (NTP)" #: lib/menu/selection_menu.py:310 lib/menu/selection_menu.py:308 #: lib/menu/selection_menu.py:315 lib/menu/selection_menu.py:527 #: lib/menu/selection_menu.py:535 lib/user_interaction/global_menu.py:187 #: lib/menu/selection_menu.py:608 lib/menu/global_menu.py:187 +#: lib/menu/global_menu.py:190 msgid "Install ({} config(s) missing)" msgstr "Установить ({} конфигурация(и) отсутствует)" @@ -644,6 +692,7 @@ msgstr "Установить ({} конфигурация(и) отсутству #: lib/menu/selection_menu.py:378 lib/menu/selection_menu.py:591 #: lib/menu/selection_menu.py:599 lib/user_interaction/global_menu.py:249 #: lib/menu/selection_menu.py:669 lib/menu/global_menu.py:249 +#: lib/menu/global_menu.py:260 msgid "" "You decided to skip harddrive selection\n" "and will use whatever drive-setup is mounted at {} (experimental)\n" @@ -657,6 +706,7 @@ msgstr "" "Вы хотите продолжить?" #: lib/disk/filesystem.py:97 lib/disk/filesystem.py:98 +#: lib/disk/filesystem.py:96 msgid "Re-using partition instance: {}" msgstr "Повторное использование экземпляра раздела: {}" @@ -664,6 +714,7 @@ msgstr "Повторное использование экземпляра ра #: lib/user_interaction.py:596 lib/user_interaction.py:595 #: lib/user_interaction/partitioning_conf.py:119 #: lib/user_interaction/partitioning_conf.py:120 +#: lib/user_interaction/partitioning_conf.py:128 msgid "Create a new partition" msgstr "Создать новый раздел" @@ -671,6 +722,7 @@ msgstr "Создать новый раздел" #: lib/user_interaction.py:598 lib/user_interaction.py:597 #: lib/user_interaction/partitioning_conf.py:121 #: lib/user_interaction/partitioning_conf.py:122 +#: lib/user_interaction/partitioning_conf.py:130 msgid "Delete a partition" msgstr "Удалить раздел" @@ -678,6 +730,7 @@ msgstr "Удалить раздел" #: lib/user_interaction.py:599 lib/user_interaction.py:598 #: lib/user_interaction/partitioning_conf.py:122 #: lib/user_interaction/partitioning_conf.py:123 +#: lib/user_interaction/partitioning_conf.py:131 msgid "Clear/Delete all partitions" msgstr "Очистить/удалить все разделы" @@ -685,6 +738,7 @@ msgstr "Очистить/удалить все разделы" #: lib/user_interaction.py:600 lib/user_interaction.py:599 #: lib/user_interaction/partitioning_conf.py:123 #: lib/user_interaction/partitioning_conf.py:124 +#: lib/user_interaction/partitioning_conf.py:132 msgid "Assign mount-point for a partition" msgstr "Назначить точку монтирования для раздела" @@ -692,6 +746,7 @@ msgstr "Назначить точку монтирования для разде #: lib/user_interaction.py:601 lib/user_interaction.py:600 #: lib/user_interaction/partitioning_conf.py:124 #: lib/user_interaction/partitioning_conf.py:125 +#: lib/user_interaction/partitioning_conf.py:133 msgid "Mark/Unmark a partition to be formatted (wipes data)" msgstr "" "Пометить/снять отметку с раздела, который будет отформатирован (стирание " @@ -701,6 +756,7 @@ msgstr "" #: lib/user_interaction.py:602 lib/user_interaction.py:601 #: lib/user_interaction/partitioning_conf.py:125 #: lib/user_interaction/partitioning_conf.py:126 +#: lib/user_interaction/partitioning_conf.py:134 msgid "Mark/Unmark a partition as encrypted" msgstr "Пометить/снять отметку с раздела как зашифрованный" @@ -708,6 +764,7 @@ msgstr "Пометить/снять отметку с раздела как за #: lib/user_interaction.py:603 lib/user_interaction.py:602 #: lib/user_interaction/partitioning_conf.py:126 #: lib/user_interaction/partitioning_conf.py:127 +#: lib/user_interaction/partitioning_conf.py:136 msgid "Mark/Unmark a partition as bootable (automatic for /boot)" msgstr "" "Пометить/снять отметку с раздела как загрузочный (автоматически для /boot)" @@ -716,6 +773,7 @@ msgstr "" #: lib/user_interaction.py:604 lib/user_interaction.py:603 #: lib/user_interaction/partitioning_conf.py:127 #: lib/user_interaction/partitioning_conf.py:128 +#: lib/user_interaction/partitioning_conf.py:137 msgid "Set desired filesystem for a partition" msgstr "Установите желаемую файловую систему для раздела" @@ -723,31 +781,33 @@ msgstr "Установите желаемую файловую систему д #: lib/menu/selection_menu.py:241 lib/menu/selection_menu.py:270 #: lib/menu/selection_menu.py:507 lib/menu/selection_menu.py:515 #: lib/user_interaction/global_menu.py:167 lib/menu/selection_menu.py:588 -#: lib/menu/global_menu.py:167 +#: lib/menu/global_menu.py:167 lib/menu/global_menu.py:170 msgid "Abort" msgstr "Прервать" #: lib/menu/selection_menu.py:183 lib/menu/selection_menu.py:187 #: lib/menu/selection_menu.py:451 lib/menu/selection_menu.py:459 #: lib/user_interaction/global_menu.py:89 lib/menu/selection_menu.py:509 -#: lib/menu/global_menu.py:89 +#: lib/menu/global_menu.py:89 lib/menu/global_menu.py:88 msgid "Specify hostname" msgstr "Укажите имя хоста" #: lib/menu/selection_menu.py:228 lib/menu/selection_menu.py:492 #: lib/menu/selection_menu.py:500 lib/user_interaction/global_menu.py:141 #: lib/menu/selection_menu.py:562 lib/menu/global_menu.py:141 +#: lib/menu/global_menu.py:195 msgid "Not configured, unavailable unless setup manually" msgstr "Не настроен, недоступен, если не настроен вручную" #: lib/menu/selection_menu.py:231 lib/menu/selection_menu.py:495 #: lib/menu/selection_menu.py:503 lib/user_interaction/global_menu.py:145 #: lib/menu/selection_menu.py:566 lib/menu/global_menu.py:145 +#: lib/menu/global_menu.py:148 msgid "Select timezone" msgstr "Выберите часовой пояс" #: lib/menu/selection_menu.py:266 lib/menu/selection_menu.py:276 -#: lib/menu/selection_menu.py:312 +#: lib/menu/selection_menu.py:312 lib/menu/selection_menu.py:301 msgid "Set/Modify the below options" msgstr "Установить/изменить следующие параметры" @@ -756,6 +816,7 @@ msgid "Install" msgstr "Установить" #: lib/menu/menu.py:68 lib/menu/menu.py:75 lib/menu/menu.py:119 +#: lib/menu/menu.py:133 msgid "" "Use ESC to skip\n" "\n" @@ -767,20 +828,24 @@ msgstr "" #: lib/user_interaction.py:597 lib/user_interaction.py:596 #: lib/user_interaction/partitioning_conf.py:120 #: lib/user_interaction/partitioning_conf.py:121 +#: lib/user_interaction/partitioning_conf.py:129 msgid "Suggest partition layout" msgstr "Предложить разметку разделов" #: lib/user_interaction.py:101 lib/user_interaction/utils.py:43 +#: lib/user_interaction/utils.py:40 msgid "Enter a password: " msgstr "Введите пароль: " #: lib/disk/filesystem.py:117 lib/disk/filesystem.py:118 +#: lib/disk/filesystem.py:116 msgid "Enter a encryption password for {}" msgstr "Введите пароль шифрования для {}" #: lib/menu/selection_menu.py:174 lib/menu/selection_menu.py:567 #: lib/menu/selection_menu.py:575 lib/user_interaction/global_menu.py:225 #: lib/menu/selection_menu.py:646 lib/menu/global_menu.py:225 +#: lib/menu/global_menu.py:236 msgid "Enter disk encryption password (leave blank for no encryption): " msgstr "" "Введите пароль шифрования диска (оставьте пустым для отсутствия шифрования): " @@ -793,12 +858,15 @@ msgstr "Создайте необходимого суперпользовате #: lib/menu/selection_menu.py:347 lib/menu/selection_menu.py:554 #: lib/menu/selection_menu.py:562 lib/user_interaction/global_menu.py:220 #: lib/menu/selection_menu.py:641 lib/menu/global_menu.py:220 +#: lib/menu/global_menu.py:231 msgid "Enter root password (leave blank to disable root): " msgstr "Введите пароль root (оставьте пустым, чтобы отключить root): " #: lib/user_interaction.py:307 lib/user_interaction.py:324 #: lib/user_interaction/manage_users_conf.py:86 #: lib/user_interaction/manage_users_conf.py:121 +#: lib/user_interaction/manage_users_conf.py:83 +#: lib/user_interaction/manage_users_conf.py:118 msgid "Password for user \"{}\": " msgstr "Пароль для пользователя \"{}\": " @@ -825,7 +893,7 @@ msgid "" msgstr "" "Для работы NTP может потребоваться аппаратное время и другие шаги после " "конфигурации.\n" -"Для получения дополнительной информации, пожалуйста, ознакомьтесь с вики Arch" +"Для получения дополнительной информации, пожалуйста, ознакомьтесь с ArchWiki" #: lib/menu/selection_menu.py:411 lib/menu/selection_menu.py:621 #: lib/menu/selection_menu.py:629 @@ -834,11 +902,11 @@ msgstr "" "Введите имя пользователя для создания дополнительного пользователя (оставьте " "пустым, чтобы пропустить): " -#: lib/menu/menu.py:116 +#: lib/menu/menu.py:116 lib/menu/menu.py:130 msgid "Use ESC to skip\n" msgstr "Используйте ESC, чтобы пропустить\n" -#: lib/menu/list_manager.py:117 +#: lib/menu/list_manager.py:117 lib/menu/list_manager.py:128 msgid "" "\n" " Choose an object from the list, and select one of the available actions for " @@ -849,10 +917,11 @@ msgstr "" "выполнения" #: lib/menu/list_manager.py:130 lib/user_interaction/subvolume_config.py:90 +#: lib/menu/list_manager.py:141 lib/user_interaction/subvolume_config.py:93 msgid "Cancel" msgstr "Отменить" -#: lib/menu/list_manager.py:131 +#: lib/menu/list_manager.py:131 lib/menu/list_manager.py:142 msgid "Confirm and exit" msgstr "Подтвердить и выйти" @@ -862,63 +931,75 @@ msgstr "Подтвердить и выйти" #: lib/user_interaction/subvolume_config.py:44 #: lib/user_interaction/subvolume_config.py:78 #: lib/user_interaction/subvolume_config.py:82 -#: lib/user_interaction/subvolume_config.py:85 +#: lib/user_interaction/subvolume_config.py:85 lib/menu/list_manager.py:146 +#: lib/menu/list_manager.py:249 lib/menu/list_manager.py:272 +#: lib/user_interaction/subvolume_config.py:15 +#: lib/user_interaction/subvolume_config.py:47 +#: lib/user_interaction/subvolume_config.py:81 +#: lib/user_interaction/subvolume_config.py:88 msgid "Add" msgstr "Добавить" #: lib/menu/list_manager.py:135 lib/menu/list_manager.py:231 #: lib/menu/list_manager.py:255 lib/user_interaction/subvolume_config.py:78 +#: lib/menu/list_manager.py:146 lib/menu/list_manager.py:252 +#: lib/menu/list_manager.py:276 lib/user_interaction/subvolume_config.py:81 msgid "Copy" msgstr "Копировать" #: lib/menu/list_manager.py:135 lib/menu/list_manager.py:237 #: lib/menu/list_manager.py:261 lib/user_interaction/subvolume_config.py:82 -#: lib/user_interaction/subvolume_config.py:85 +#: lib/user_interaction/subvolume_config.py:85 lib/menu/list_manager.py:146 +#: lib/menu/list_manager.py:258 lib/menu/list_manager.py:282 +#: lib/user_interaction/subvolume_config.py:88 msgid "Edit" msgstr "Редактировать" #: lib/menu/list_manager.py:135 lib/menu/list_manager.py:242 #: lib/menu/list_manager.py:264 lib/user_interaction/subvolume_config.py:40 +#: lib/menu/list_manager.py:146 lib/menu/list_manager.py:263 +#: lib/menu/list_manager.py:285 lib/user_interaction/subvolume_config.py:44 msgid "Delete" msgstr "Удалить" -#: lib/menu/list_manager.py:185 +#: lib/menu/list_manager.py:185 lib/menu/list_manager.py:206 msgid "Select an action for < {} >" msgstr "Выберите действие для < {} >" -#: lib/menu/list_manager.py:229 +#: lib/menu/list_manager.py:229 lib/menu/list_manager.py:250 msgid "Add :" msgstr "Добавить:" -#: lib/menu/list_manager.py:233 +#: lib/menu/list_manager.py:233 lib/menu/list_manager.py:254 msgid "Copy to :" msgstr "Копировать в:" -#: lib/menu/list_manager.py:240 +#: lib/menu/list_manager.py:240 lib/menu/list_manager.py:261 msgid "Edite :" msgstr "Редактировать:" -#: lib/menu/list_manager.py:252 +#: lib/menu/list_manager.py:252 lib/menu/list_manager.py:273 msgid "Key :" msgstr "Ключ:" -#: lib/menu/list_manager.py:253 +#: lib/menu/list_manager.py:253 lib/menu/list_manager.py:274 msgid "Value :" msgstr "Значение:" -#: lib/menu/list_manager.py:257 +#: lib/menu/list_manager.py:257 lib/menu/list_manager.py:278 msgid "Copy to new key:" msgstr "Копировать в новый ключ:" -#: lib/menu/list_manager.py:262 +#: lib/menu/list_manager.py:262 lib/menu/list_manager.py:283 #, python-brace-format msgid "Edit {origkey} :" msgstr "Редактировать {origkey}:" #: lib/models/network_configuration.py:53 #: lib/models/network_configuration.py:74 +#: lib/models/network_configuration.py:167 msgid "Unknown nic type: {}. Possible values are {}" -msgstr "Неизвестный тип nic: {}. Возможные значения: {}" +msgstr "Неизвестный тип сетевого адаптера: {}. Возможные значения: {}" #: lib/configuration.py:75 msgid "" @@ -963,18 +1044,21 @@ msgid "Delete User" msgstr "Удалить пользователя" #: lib/user_interaction/manage_users_conf.py:99 +#: lib/user_interaction/manage_users_conf.py:96 msgid "" "\n" "Define a new user\n" msgstr "" "\n" -"Определить нового пользователя\n" +"Укажите нового пользователя\n" #: lib/user_interaction/manage_users_conf.py:100 +#: lib/user_interaction/manage_users_conf.py:97 msgid "User Name : " msgstr "Имя пользователя: " #: lib/user_interaction/manage_users_conf.py:115 +#: lib/user_interaction/manage_users_conf.py:112 msgid "Should {} be a superuser (sudoer)?" msgstr "Должен ли {} быть суперпользователем (sudoer)?" @@ -983,16 +1067,19 @@ msgid "Define users with sudo privilege: " msgstr "Определите пользователей с привилегиями sudo: " #: lib/user_interaction/network_conf.py:24 +#: lib/user_interaction/network_conf.py:124 msgid "No network configuration" msgstr "Нет сетевой конфигурации" #: lib/user_interaction/partitioning_conf.py:128 #: lib/user_interaction/partitioning_conf.py:129 +#: lib/user_interaction/partitioning_conf.py:138 msgid "Set desired subvolumes on a btrfs partition" msgstr "Установите желаемые подтома на раздел btrfs" #: lib/user_interaction/partitioning_conf.py:296 #: lib/user_interaction/partitioning_conf.py:303 +#: lib/user_interaction/partitioning_conf.py:336 msgid "" "{}\n" "\n" @@ -1004,6 +1091,7 @@ msgstr "" #: lib/user_interaction/partitioning_conf.py:305 #: lib/user_interaction/partitioning_conf.py:312 +#: lib/user_interaction/partitioning_conf.py:346 msgid "Manage btrfs subvolumes for current partition" msgstr "Управление подтомами btrfs для текущего раздела" @@ -1045,53 +1133,57 @@ msgstr "Пароль, который вы используете, кажется #: lib/user_interaction/utils.py:33 msgid "are you sure you want to use it?" -msgstr "вы уверены, что хотите это использовать?" +msgstr "вы уверены, что хотите его использовать?" #: lib/user_interaction/global_menu.py:134 lib/menu/selection_menu.py:555 -#: lib/menu/global_menu.py:134 +#: lib/menu/global_menu.py:134 lib/menu/global_menu.py:137 msgid "Additional repositories to enable" msgstr "Включить дополнительные репозитории" #: lib/user_interaction/global_menu.py:155 lib/menu/selection_menu.py:576 -#: lib/menu/global_menu.py:155 +#: lib/menu/global_menu.py:155 lib/menu/global_menu.py:160 msgid "Save configuration" msgstr "Сохранить конфигурацию" #: lib/user_interaction/global_menu.py:192 lib/menu/selection_menu.py:613 -#: lib/menu/global_menu.py:192 +#: lib/menu/global_menu.py:192 lib/menu/global_menu.py:205 msgid "Missing configurations:\n" msgstr "Отсутствующие конфигурации:\n" #: lib/user_interaction/global_menu.py:210 lib/menu/selection_menu.py:631 -#: lib/menu/global_menu.py:210 +#: lib/menu/global_menu.py:210 lib/menu/global_menu.py:221 msgid "Either root-password or at least 1 superuser must be specified" msgstr "" "Должен быть указан либо пароль root, либо как минимум 1 суперпользователь" #: lib/user_interaction/global_menu.py:275 lib/menu/selection_menu.py:695 -#: lib/menu/global_menu.py:275 +#: lib/menu/global_menu.py:275 lib/menu/global_menu.py:287 msgid "Manage superuser accounts: " msgstr "Управление учетными записями суперпользователей: " #: lib/user_interaction/global_menu.py:279 lib/menu/selection_menu.py:699 -#: lib/menu/global_menu.py:279 +#: lib/menu/global_menu.py:279 lib/menu/global_menu.py:291 msgid "Manage ordinary user accounts: " msgstr "Управление учетными записями обычных пользователей: " #: lib/user_interaction/subvolume_config.py:17 +#: lib/user_interaction/subvolume_config.py:20 msgid " Subvolume :{:16}" msgstr " Подтом :{:16}" #: lib/user_interaction/subvolume_config.py:19 #: lib/user_interaction/subvolume_config.py:22 +#: lib/user_interaction/subvolume_config.py:25 msgid " mounted at {:16}" msgstr " смонтировано в {:16}" #: lib/user_interaction/subvolume_config.py:26 +#: lib/user_interaction/subvolume_config.py:29 msgid " with option {}" msgstr " с параметром {}" #: lib/user_interaction/subvolume_config.py:46 +#: lib/user_interaction/subvolume_config.py:49 msgid "" "\n" " Fill the desired values for a new subvolume \n" @@ -1100,37 +1192,195 @@ msgstr "" " Заполните нужные значения для нового подтома\n" #: lib/user_interaction/subvolume_config.py:77 +#: lib/user_interaction/subvolume_config.py:80 msgid "Subvolume name " msgstr "Имя подтома " #: lib/user_interaction/subvolume_config.py:81 +#: lib/user_interaction/subvolume_config.py:84 msgid "Subvolume mountpoint" msgstr "Точка монтирования подтома" #: lib/user_interaction/subvolume_config.py:84 +#: lib/user_interaction/subvolume_config.py:87 msgid "Subvolume options" msgstr "Параметры подтома" #: lib/user_interaction/subvolume_config.py:87 +#: lib/user_interaction/subvolume_config.py:90 msgid "Save" msgstr "Сохранить" #: lib/user_interaction/subvolume_config.py:121 +#: lib/user_interaction/subvolume_config.py:124 msgid "Subvolume name :" msgstr "Имя подтома :" #: lib/user_interaction/subvolume_config.py:124 +#: lib/user_interaction/subvolume_config.py:127 msgid "Select a mount point :" msgstr "Выберите точку монтирования:" #: lib/user_interaction/subvolume_config.py:128 +#: lib/user_interaction/subvolume_config.py:131 msgid "Select the desired subvolume options " msgstr "Выберите нужные параметры подтома " #: lib/user_interaction/manage_users_conf.py:161 +#: lib/user_interaction/manage_users_conf.py:158 msgid "Define users with sudo privilege, by username: " msgstr "Определите пользователей с привилегией sudo по имени пользователя: " -#: lib/installer.py:152 +#: lib/installer.py:152 lib/installer.py:154 msgid "[!] A log file has been created here: {}" msgstr "[!] Здесь был создан файл журнала: {}" + +#: lib/disk/user_guides.py:30 +msgid "Would you like to use BTRFS subvolumes with a default structure?" +msgstr "Вы хотите использовать подтома BTRFS со структурой по умолчанию?" + +#: lib/disk/user_guides.py:34 lib/disk/user_guides.py:174 +msgid "Would you like to use BTRFS compression?" +msgstr "Хотите ли вы использовать сжатие BTRFS?" + +#: lib/disk/user_guides.py:91 +msgid "Would you like to create a separate partition for /home?" +msgstr "Хотите ли вы создать отдельный раздел для /home?" + +#: lib/disk/user_guides.py:161 +msgid "" +"The selected drives do not have the minimum capacity required for an " +"automatic suggestion\n" +msgstr "" +"Выбранные диски не имеют минимальной емкости, необходимой для " +"автоматического предложения\n" + +#: lib/disk/user_guides.py:162 +msgid "Minimum capacity for /home partition: {}GB\n" +msgstr "Минимальный размер раздела /home: {}GB\n" + +#: lib/disk/user_guides.py:163 +msgid "Minimum capacity for Arch Linux partition: {}GB" +msgstr "Минимальный размер раздела Arch Linux: {}GB" + +#: lib/disk/user_guides.py:164 +msgid "Continue" +msgstr "Продолжить" + +#: lib/menu/menu.py:20 +msgid "yes" +msgstr "да" + +#: lib/menu/menu.py:24 +msgid "no" +msgstr "нет" + +#: lib/menu/selection_menu.py:137 +msgid "set: {}" +msgstr "выбор: {}" + +#: lib/models/network_configuration.py:118 +msgid "Manual configuration setting must be a list" +msgstr "Ручная настройка конфигурации должна представлять собой список" + +#: lib/models/network_configuration.py:127 +msgid "No iface specified for manual configuration" +msgstr "Не указан iface для ручной настройки" + +#: lib/models/network_configuration.py:137 +msgid "Manual nic configuration with no auto DHCP requires an IP address" +msgstr "" +"Ручная конфигурация сетевого адаптера без автоматического DHCP требует IP-" +"адреса" + +#: lib/user_interaction/network_conf.py:37 +msgid "Add interface" +msgstr "Добавить интерфейс" + +#: lib/user_interaction/network_conf.py:38 +msgid "Edit interface" +msgstr "Редактировать интерфейс" + +#: lib/user_interaction/network_conf.py:39 +msgid "Delete interface" +msgstr "Удалить интерфейс" + +#: lib/user_interaction/network_conf.py:69 +msgid "Select interface to add" +msgstr "Выберите интерфейс для добавления" + +#: lib/user_interaction/network_conf.py:127 +msgid "Manual configuration" +msgstr "Ручная конфигурация" + +#: lib/user_interaction/partitioning_conf.py:135 +msgid "Mark/Unmark a partition as compressed (btrfs only)" +msgstr "Пометить/снять отметку с раздела как сжатый (только для btrfs)" + +#: lib/user_interaction/utils.py:31 +msgid "" +"The password you are using seems to be weak, are you sure you want to use it?" +msgstr "" +"Пароль, который вы используете, кажется слабым, вы уверены, что хотите его " +"использовать?" + +#: profiles/desktop.py:7 +msgid "" +"Provides a selection of desktop environments and tiling window managers, e." +"g. gnome, kde, sway" +msgstr "" +"Предоставляет выбор окружений рабочего стола и тайловых оконных менеджеров, " +"например, gnome, kde, sway" + +#: profiles/desktop.py:49 +msgid "Select your desired desktop environment" +msgstr "Выберите желаемое окружение рабочего стола" + +#: profiles/minimal.py:5 +msgid "" +"A very basic installation that allows you to customize Arch Linux as you see " +"fit." +msgstr "" +"Очень базовая установка, позволяющая настроить Arch Linux по своему " +"усмотрению." + +#: profiles/server.py:9 +msgid "" +"Provides a selection of various server packages to install and enable, e.g. " +"httpd, nginx, mariadb" +msgstr "" +"Предоставляет выбор различных пакетов сервера для установки и включения, " +"например, httpd, nginx, mariadb" + +#: profiles/server.py:30 +msgid "" +"Choose which servers to install, if none then a minimal installation wil be " +"done" +msgstr "" +"Выберите серверы для установки, если их нет, то будет выполнена минимальная " +"установка" + +#: profiles/xorg.py:9 +msgid "Installs a minimal system as well as xorg and graphics drivers." +msgstr "" +"Устанавливает минимальную систему, а также xorg и графические драйверы." + +#: examples/guided.py:303 +msgid "Press Enter to continue." +msgstr "Нажмите Enter, чтобы продолжить." + +#: examples/guided.py:260 +msgid "" +"Would you like to chroot into the newly created installation and perform " +"post-installation configuration?" +msgstr "" +"Хотите ли вы использовать chroot в новой созданной установке и выполнить " +"настройку после установки?" + +#~ msgid "Archinstall requires root privileges to run. See --help for more." +#~ msgstr "" +#~ "Для запуска Archinstall требуются привилегии root. Для получения " +#~ "дополнительной информации смотрите --help." + +#~ msgid " ! Formatting {archinstall.arguments['harddrives']} in " +#~ msgstr " ! Форматирование {archinstall.arguments['harddrives']} в " diff --git a/examples/guided.py b/examples/guided.py index 1cee499d..b653ea2a 100644 --- a/examples/guided.py +++ b/examples/guided.py @@ -257,7 +257,7 @@ def perform_installation(mountpoint): installation.log("For post-installation tips, see https://wiki.archlinux.org/index.php/Installation_guide#Post-installation", fg="yellow") if not archinstall.arguments.get('silent'): - prompt = 'Would you like to chroot into the newly created installation and perform post-installation configuration?' + prompt = str(_('Would you like to chroot into the newly created installation and perform post-installation configuration?')) choice = Menu(prompt, Menu.yes_no(), default_option=Menu.yes()).run() if choice == Menu.yes(): try: @@ -300,7 +300,7 @@ if archinstall.arguments.get('dry_run'): exit(0) if not archinstall.arguments.get('silent'): - input('Press Enter to continue.') + input(str(_('Press Enter to continue.'))) perform_filesystem_operations() perform_installation(archinstall.storage.get('MOUNT_POINT', '/mnt')) diff --git a/profiles/desktop.py b/profiles/desktop.py index 977fe2de..eaece9f5 100644 --- a/profiles/desktop.py +++ b/profiles/desktop.py @@ -4,7 +4,7 @@ from archinstall import log is_top_level_profile = True -__description__ = 'Provides a selection of desktop environments and tiling window managers, e.g. gnome, kde, sway' +__description__ = str(_('Provides a selection of desktop environments and tiling window managers, e.g. gnome, kde, sway')) # New way of defining packages for a profile, which is iterable and can be used out side # of the profile to get a list of "what packages will be installed". @@ -46,7 +46,7 @@ def _prep_function(*args, **kwargs) -> bool: other code in this stage. So it's a safe way to ask the user for more input before any other installer steps start. """ - desktop = archinstall.Menu('Select your desired desktop environment', __supported__).run() + desktop = archinstall.Menu(str(_('Select your desired desktop environment')), __supported__).run() if desktop: # Temporarily store the selected desktop profile diff --git a/profiles/minimal.py b/profiles/minimal.py index c7df517c..3b161511 100644 --- a/profiles/minimal.py +++ b/profiles/minimal.py @@ -2,7 +2,7 @@ is_top_level_profile = True -__description__ = 'A very basic installation that allows you to customize Arch Linux as you see fit.' +__description__ = str(_('A very basic installation that allows you to customize Arch Linux as you see fit.')) def _prep_function(*args, **kwargs): diff --git a/profiles/server.py b/profiles/server.py index bbeece12..91ac7cf2 100644 --- a/profiles/server.py +++ b/profiles/server.py @@ -6,7 +6,7 @@ import archinstall is_top_level_profile = True -__description__ = 'Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb' +__description__ = str(_('Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb')) available_servers = [ "cockpit", @@ -26,8 +26,8 @@ def _prep_function(*args, **kwargs): Magic function called by the importing installer before continuing any further. """ - servers = archinstall.Menu( - 'Choose which servers to install, if none then a minimal installation wil be done', + servers = archinstall.Menu(str(_( + 'Choose which servers to install, if none then a minimal installation wil be done')), available_servers, preset_values=archinstall.storage.get('_selected_servers', []), multi=True diff --git a/profiles/xorg.py b/profiles/xorg.py index 237e4350..2ce8dcc2 100644 --- a/profiles/xorg.py +++ b/profiles/xorg.py @@ -6,7 +6,7 @@ from archinstall.lib.hardware import __packages__ as __hwd__packages__ is_top_level_profile = True -__description__ = 'Installs a minimal system as well as xorg and graphics drivers.' +__description__ = str(_('Installs a minimal system as well as xorg and graphics drivers.')) __packages__ = [ 'dkms', -- cgit v1.2.3-70-g09d2 From 0fa52a5424e28ed62ef84bdc92868bbfc434e015 Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Mon, 9 May 2022 20:02:48 +1000 Subject: Introduce ctrl+c and other bug fixes (#1152) * Intergrate ctrl+c * stash * Update * Fix profile reset * flake8 Co-authored-by: Daniel Girtler --- archinstall/lib/disk/user_guides.py | 8 +- archinstall/lib/menu/global_menu.py | 94 ++++++++++---- archinstall/lib/menu/list_manager.py | 29 +++-- archinstall/lib/menu/menu.py | 144 ++++++++++++++------- archinstall/lib/menu/selection_menu.py | 55 ++++---- archinstall/lib/menu/simple_menu.py | 15 ++- archinstall/lib/profiles.py | 4 + archinstall/lib/user_interaction/disk_conf.py | 32 +++-- archinstall/lib/user_interaction/general_conf.py | 127 +++++++++++------- archinstall/lib/user_interaction/locale_conf.py | 35 +++-- archinstall/lib/user_interaction/network_conf.py | 37 ++++-- .../lib/user_interaction/partitioning_conf.py | 81 ++++++------ archinstall/lib/user_interaction/save_conf.py | 27 ++-- archinstall/lib/user_interaction/system_conf.py | 113 ++++++++++------ archinstall/lib/user_interaction/utils.py | 6 +- examples/guided.py | 5 +- examples/swiss.py | 2 +- profiles/desktop.py | 25 ++-- profiles/i3.py | 13 +- profiles/minimal.py | 2 + profiles/server.py | 17 ++- profiles/sway.py | 5 +- 22 files changed, 569 insertions(+), 307 deletions(-) (limited to 'profiles') diff --git a/archinstall/lib/disk/user_guides.py b/archinstall/lib/disk/user_guides.py index 77da52e4..5fa6bfdc 100644 --- a/archinstall/lib/disk/user_guides.py +++ b/archinstall/lib/disk/user_guides.py @@ -29,11 +29,11 @@ def suggest_single_disk_layout(block_device :BlockDevice, if default_filesystem == 'btrfs': prompt = str(_('Would you like to use BTRFS subvolumes with a default structure?')) choice = Menu(prompt, Menu.yes_no(), skip=False, default_option=Menu.yes()).run() - using_subvolumes = choice == Menu.yes() + using_subvolumes = choice.value == Menu.yes() prompt = str(_('Would you like to use BTRFS compression?')) choice = Menu(prompt, Menu.yes_no(), skip=False, default_option=Menu.yes()).run() - compression = choice == Menu.yes() + compression = choice.value == Menu.yes() layout = { block_device.path : { @@ -90,7 +90,7 @@ def suggest_single_disk_layout(block_device :BlockDevice, if not using_subvolumes and block_device.size >= MIN_SIZE_TO_ALLOW_HOME_PART: prompt = str(_('Would you like to create a separate partition for /home?')) choice = Menu(prompt, Menu.yes_no(), skip=False, default_option=Menu.yes()).run() - using_home_partition = choice == Menu.yes() + using_home_partition = choice.value == Menu.yes() # Set a size for / (/root) if using_subvolumes or block_device.size < MIN_SIZE_TO_ALLOW_HOME_PART or not using_home_partition: @@ -173,7 +173,7 @@ def suggest_multi_disk_layout(block_devices :List[BlockDevice], default_filesyst prompt = str(_('Would you like to use BTRFS compression?')) choice = Menu(prompt, Menu.yes_no(), skip=False, default_option=Menu.yes()).run() - compression = choice == Menu.yes() + compression = choice.value == Menu.yes() log(f"Suggesting multi-disk-layout using {len(block_devices)} disks, where {root_device} will be /root and {home_device} will be /home", level=logging.DEBUG) diff --git a/archinstall/lib/menu/global_menu.py b/archinstall/lib/menu/global_menu.py index b3c5c6a2..23a8ec11 100644 --- a/archinstall/lib/menu/global_menu.py +++ b/archinstall/lib/menu/global_menu.py @@ -2,13 +2,15 @@ from __future__ import annotations from typing import Any, List, Optional, Union +import archinstall + from ..menu import Menu from ..menu.selection_menu import Selector, GeneralMenu from ..general import SysCommand, secret from ..hardware import has_uefi from ..models import NetworkConfiguration from ..storage import storage -from ..profiles import is_desktop_profile +from ..profiles import is_desktop_profile, Profile from ..disk import encrypted_partitions from ..user_interaction import get_password, ask_for_a_timezone, save_config @@ -41,28 +43,38 @@ class GlobalMenu(GeneralMenu): self._menu_options['archinstall-language'] = \ Selector( _('Select Archinstall language'), - lambda x: self._select_archinstall_language('English'), + lambda x: self._select_archinstall_language(x), default='English') self._menu_options['keyboard-layout'] = \ - Selector(_('Select keyboard layout'), lambda preset: select_language('us',preset), default='us') + Selector( + _('Select keyboard layout'), + lambda preset: select_language(preset), + default='us') self._menu_options['mirror-region'] = \ Selector( _('Select mirror region'), - select_mirror_regions, + lambda preset: select_mirror_regions(preset), display_func=lambda x: list(x.keys()) if x else '[]', default={}) self._menu_options['sys-language'] = \ - Selector(_('Select locale language'), lambda preset: select_locale_lang('en_US',preset), default='en_US') + Selector( + _('Select locale language'), + lambda preset: select_locale_lang(preset), + default='en_US') self._menu_options['sys-encoding'] = \ - Selector(_('Select locale encoding'), lambda preset: select_locale_enc('utf-8',preset), default='utf-8') + Selector( + _('Select locale encoding'), + lambda preset: select_locale_enc(preset), + default='UTF-8') self._menu_options['harddrives'] = \ Selector( _('Select harddrives'), - self._select_harddrives) + lambda preset: self._select_harddrives(preset)) self._menu_options['disk_layouts'] = \ Selector( _('Select disk layout'), - lambda x: select_disk_layout( + lambda preset: select_disk_layout( + preset, storage['arguments'].get('harddrives', []), storage['arguments'].get('advanced', False) ), @@ -112,7 +124,7 @@ class GlobalMenu(GeneralMenu): self._menu_options['profile'] = \ Selector( _('Specify profile'), - lambda x: self._select_profile(), + lambda preset: self._select_profile(preset), display_func=lambda x: x if x else 'None') self._menu_options['audio'] = \ Selector( @@ -247,41 +259,73 @@ class GlobalMenu(GeneralMenu): return ntp def _select_harddrives(self, old_harddrives : list) -> list: - # old_haddrives = storage['arguments'].get('harddrives', []) harddrives = select_harddrives(old_harddrives) - # in case the harddrives got changed we have to reset the disk layout as well - if old_harddrives != harddrives: - self._menu_options.get('disk_layouts').set_current_selection(None) - storage['arguments']['disk_layouts'] = {} - - if not harddrives: + if len(harddrives) == 0: prompt = _( "You decided to skip harddrive selection\nand will use whatever drive-setup is mounted at {} (experimental)\n" "WARNING: Archinstall won't check the suitability of this setup\n" "Do you wish to continue?" ).format(storage['MOUNT_POINT']) - choice = Menu(prompt, Menu.yes_no(), default_option=Menu.yes()).run() + choice = Menu(prompt, Menu.yes_no(), default_option=Menu.yes(), skip=False).run() - if choice == Menu.no(): - exit(1) + if choice.value == Menu.no(): + return self._select_harddrives(old_harddrives) + + # in case the harddrives got changed we have to reset the disk layout as well + if old_harddrives != harddrives: + self._menu_options.get('disk_layouts').set_current_selection(None) + storage['arguments']['disk_layouts'] = {} return harddrives - def _select_profile(self): - profile = select_profile() + def _select_profile(self, preset): + profile = select_profile(preset) + ret = None + + if profile is None: + if any([ + archinstall.storage.get('profile_minimal', False), + archinstall.storage.get('_selected_servers', None), + archinstall.storage.get('_desktop_profile', None), + archinstall.arguments.get('desktop-environment', None), + archinstall.arguments.get('gfx_driver_packages', None) + ]): + return preset + else: # ctrl+c was actioned and all profile settings have been reset + return None + + servers = archinstall.storage.get('_selected_servers', []) + desktop = archinstall.storage.get('_desktop_profile', None) + desktop_env = archinstall.arguments.get('desktop-environment', None) + gfx_driver = archinstall.arguments.get('gfx_driver_packages', None) # Check the potentially selected profiles preparations to get early checks if some additional questions are needed. if profile and profile.has_prep_function(): namespace = f'{profile.namespace}.py' with profile.load_instructions(namespace=namespace) as imported: - if imported._prep_function(): - return profile + if imported._prep_function(servers=servers, desktop=desktop, desktop_env=desktop_env, gfx_driver=gfx_driver): + ret: Profile = profile + + match ret.name: + case 'minimal': + reset = ['_selected_servers', '_desktop_profile', 'desktop-environment', 'gfx_driver_packages'] + case 'server': + reset = ['_desktop_profile', 'desktop-environment'] + case 'desktop': + reset = ['_selected_servers'] + case 'xorg': + reset = ['_selected_servers', '_desktop_profile', 'desktop-environment'] + + for r in reset: + archinstall.storage[r] = None else: - return self._select_profile() + return self._select_profile(preset) + elif profile: + ret = profile - return self._data_store.get('profile', None) + return ret def _create_superuser_account(self): superusers = ask_for_superuser_account(str(_('Manage superuser accounts: '))) diff --git a/archinstall/lib/menu/list_manager.py b/archinstall/lib/menu/list_manager.py index 377d30f2..f3927e6f 100644 --- a/archinstall/lib/menu/list_manager.py +++ b/archinstall/lib/menu/list_manager.py @@ -86,7 +86,7 @@ The contents in the base class of this methods serve for a very basic usage, and """ from .text_input import TextInput -from .menu import Menu +from .menu import Menu, MenuSelectionType from os import system from copy import copy from typing import Union, Any, TYPE_CHECKING, Dict @@ -167,6 +167,7 @@ class ListManager: options += self.bottom_list system('clear') + target = Menu( self._prompt, options, @@ -174,27 +175,31 @@ class ListManager: clear_screen=False, clear_menu_on_exit=False, header=self.header, - skip_empty_entries=True).run() + skip_empty_entries=True + ).run() + + if target.type_ == MenuSelectionType.Esc: + return self.run() - if not target or target in self.bottom_list: + if not target.value or target.value in self.bottom_list: self.action = target break - if target and target in self._default_action: - self.action = target + if target.value and target.value in self._default_action: + self.action = target.value self.target = None self.exec_action(self._data) continue if isinstance(self._data,dict): - data_key = data_formatted[target] + data_key = data_formatted[target.value] key = self._data[data_key] self.target = {data_key: key} else: - self.target = self._data[data_formatted[target]] + self.target = self._data[data_formatted[target.value]] # Possible enhacement. If run_actions returns false a message line indicating the failure - self.run_actions(target) + self.run_actions(target.value) if not target or target == self.cancel_action: # TODO dubious return self.base_data # return the original list @@ -204,14 +209,18 @@ class ListManager: def run_actions(self,prompt_data=None): options = self.action_list() + self.bottom_item prompt = _("Select an action for < {} >").format(prompt_data if prompt_data else self.target) - self.action = Menu( + choice = Menu( prompt, options, sort=False, clear_screen=False, clear_menu_on_exit=False, preset_values=self.bottom_item, - show_search_hint=False).run() + show_search_hint=False + ).run() + + self.action = choice.value + if not self.action or self.action == self.cancel_action: return False else: diff --git a/archinstall/lib/menu/menu.py b/archinstall/lib/menu/menu.py index b2f4146d..c34814eb 100644 --- a/archinstall/lib/menu/menu.py +++ b/archinstall/lib/menu/menu.py @@ -1,4 +1,6 @@ -from typing import Dict, List, Union, Any, TYPE_CHECKING +from dataclasses import dataclass +from enum import Enum, auto +from typing import Dict, List, Union, Any, TYPE_CHECKING, Optional from archinstall.lib.menu.simple_menu import TerminalMenu @@ -13,6 +15,18 @@ if TYPE_CHECKING: _: Any +class MenuSelectionType(Enum): + Selection = auto() + Esc = auto() + Ctrl_c = auto() + + +@dataclass +class MenuSelection: + type_: MenuSelectionType + value: Optional[Union[str, List[str]]] = None + + class Menu(TerminalMenu): @classmethod @@ -33,14 +47,16 @@ class Menu(TerminalMenu): p_options :Union[List[str], Dict[str, Any]], skip :bool = True, multi :bool = False, - default_option :str = None, + default_option : Optional[str] = None, sort :bool = True, preset_values :Union[str, List[str]] = None, - cursor_index :int = None, + cursor_index : Optional[int] = None, preview_command=None, preview_size=0.75, preview_title='Info', header :Union[List[str],str] = None, + explode_on_interrupt :bool = False, + explode_warning :str = '', **kwargs ): """ @@ -80,9 +96,15 @@ class Menu(TerminalMenu): :param preview_title: Title of the preview window :type preview_title: str - param: header one or more header lines for the menu + param header: one or more header lines for the menu type param: string or list + param explode_on_interrupt: This will explicitly handle a ctrl+c instead and return that specific state + type param: bool + + param explode_warning: If explode_on_interrupt is True and this is non-empty, there will be a warning with a user confirmation displayed + type param: str + :param kwargs : any SimpleTerminal parameter """ # we guarantee the inmutability of the options outside the class. @@ -99,6 +121,8 @@ class Menu(TerminalMenu): log(f"invalid parameter at Menu() call was at <{sys._getframe(1).f_code.co_name}>",level=logging.WARNING) raise RequirementError("Menu() requires an iterable as option.") + self._default_str = str(_('(default)')) + if isinstance(p_options,dict): options = list(p_options.keys()) else: @@ -117,27 +141,40 @@ class Menu(TerminalMenu): if sort: options = sorted(options) - self.menu_options = options - self.skip = skip - self.default_option = default_option - self.multi = multi + self._menu_options = options + self._skip = skip + self._default_option = default_option + self._multi = multi + self._explode_on_interrupt = explode_on_interrupt + self._explode_warning = explode_warning + menu_title = f'\n{title}\n\n' + if header: - separator = '\n ' if not isinstance(header,(list,tuple)): - header = [header,] - if skip: - menu_title += str(_("Use ESC to skip\n")) - menu_title += separator + separator.join(header) - elif skip: - menu_title += str(_("Use ESC to skip\n\n")) + header = [header] + header = '\n'.join(header) + menu_title += f'\n{header}\n' + + action_info = '' + if skip: + action_info += str(_("Use ESC to skip")) + + if self._explode_on_interrupt: + if len(action_info) > 0: + action_info += '\n' + action_info += str(_('Use CTRL+C to reset current selection\n\n')) + + menu_title += action_info + if default_option: # if a default value was specified we move that one # to the top of the list and mark it as default as well - default = f'{default_option} (default)' - self.menu_options = [default] + [o for o in self.menu_options if default_option != o] + default = f'{default_option} {self._default_str}' + self._menu_options = [default] + [o for o in self._menu_options if default_option != o] + + self._preselection(preset_values,cursor_index) - self.preselection(preset_values,cursor_index) cursor = "> " main_menu_cursor_style = ("fg_cyan", "bold") main_menu_style = ("bg_blue", "fg_gray") @@ -145,8 +182,9 @@ class Menu(TerminalMenu): kwargs['clear_screen'] = kwargs.get('clear_screen',True) kwargs['show_search_hint'] = kwargs.get('show_search_hint',True) kwargs['cycle_cursor'] = kwargs.get('cycle_cursor',True) + super().__init__( - menu_entries=self.menu_options, + menu_entries=self._menu_options, title=menu_title, menu_cursor=cursor, menu_cursor_style=main_menu_cursor_style, @@ -160,32 +198,46 @@ class Menu(TerminalMenu): preview_command=preview_command, preview_size=preview_size, preview_title=preview_title, + explode_on_interrupt=self._explode_on_interrupt, multi_select_select_on_accept=False, **kwargs, ) - def _show(self): - idx = self.show() + def _show(self) -> MenuSelection: + try: + idx = self.show() + except KeyboardInterrupt: + return MenuSelection(type_=MenuSelectionType.Ctrl_c) + + def check_default(elem): + if self._default_option is not None and f'{self._default_option} {self._default_str}' in elem: + return self._default_option + else: + return elem + if idx is not None: if isinstance(idx, (list, tuple)): - return [self.default_option if ' (default)' in self.menu_options[i] else self.menu_options[i] for i in idx] + results = [] + for i in idx: + option = check_default(self._menu_options[i]) + results.append(option) + return MenuSelection(type_=MenuSelectionType.Selection, value=results) else: - selected = self.menu_options[idx] - if ' (default)' in selected and self.default_option: - return self.default_option - return selected + result = check_default(self._menu_options[idx]) + return MenuSelection(type_=MenuSelectionType.Selection, value=result) else: - if self.default_option: - if self.multi: - return [self.default_option] - else: - return self.default_option - return None - - def run(self): + return MenuSelection(type_=MenuSelectionType.Esc) + + def run(self) -> MenuSelection: ret = self._show() - if ret is None and not self.skip: + if ret.type_ == MenuSelectionType.Ctrl_c: + if self._explode_on_interrupt and len(self._explode_warning) > 0: + response = Menu(self._explode_warning, Menu.yes_no(), skip=False).run() + if response.value == Menu.no(): + return self.run() + + if ret.type_ is not MenuSelectionType.Selection and not self._skip: return self.run() return ret @@ -200,15 +252,15 @@ class Menu(TerminalMenu): pos = self._menu_entries.index(value) self.set_cursor_pos(pos) - def preselection(self,preset_values :list = [],cursor_index :int = None): + def _preselection(self,preset_values :Union[str, List[str]] = [], cursor_index : Optional[int] = None): def from_preset_to_cursor(): if preset_values: # if the value is not extant return 0 as cursor index try: if isinstance(preset_values,str): - self.cursor_index = self.menu_options.index(self.preset_values) + self.cursor_index = self._menu_options.index(self.preset_values) else: # should return an error, but this is smoother - self.cursor_index = self.menu_options.index(self.preset_values[0]) + self.cursor_index = self._menu_options.index(self.preset_values[0]) except ValueError: self.cursor_index = 0 @@ -218,13 +270,13 @@ class Menu(TerminalMenu): return self.preset_values = preset_values - if self.default_option: - if isinstance(preset_values,str) and self.default_option == preset_values: - self.preset_values = f"{preset_values} (default)" - elif isinstance(preset_values,(list,tuple)) and self.default_option in preset_values: - idx = preset_values.index(self.default_option) - self.preset_values[idx] = f"{preset_values[idx]} (default)" - if cursor_index is None or not self.multi: + if self._default_option: + if isinstance(preset_values,str) and self._default_option == preset_values: + self.preset_values = f"{preset_values} {self._default_str}" + elif isinstance(preset_values,(list,tuple)) and self._default_option in preset_values: + idx = preset_values.index(self._default_option) + self.preset_values[idx] = f"{preset_values[idx]} {self._default_str}" + if cursor_index is None or not self._multi: from_preset_to_cursor() - if not self.multi: # Not supported by the infraestructure + if not self._multi: # Not supported by the infraestructure self.preset_values = None diff --git a/archinstall/lib/menu/selection_menu.py b/archinstall/lib/menu/selection_menu.py index e18d92c2..54b1441b 100644 --- a/archinstall/lib/menu/selection_menu.py +++ b/archinstall/lib/menu/selection_menu.py @@ -4,7 +4,7 @@ import logging import sys from typing import Callable, Any, List, Iterator, Tuple, Optional, Dict, TYPE_CHECKING -from .menu import Menu +from .menu import Menu, MenuSelectionType from ..locale_helpers import set_keyboard_language from ..output import log from ..translation import Translation @@ -12,13 +12,15 @@ from ..translation import Translation if TYPE_CHECKING: _: Any -def select_archinstall_language(default='English'): + +def select_archinstall_language(preset_value: str) -> Optional[str]: """ copied from user_interaction/general_conf.py as a temporary measure """ languages = Translation.get_all_names() - language = Menu(_('Select Archinstall language'), languages, default_option=default).run() - return language + language = Menu(_('Select Archinstall language'), languages, preset_values=preset_value).run() + return language.value + class Selector: def __init__( @@ -307,22 +309,27 @@ class GeneralMenu: skip_empty_entries=True ).run() - if selection and self.auto_cursor: - cursor_pos = menu_options.index(selection) + 1 # before the strip otherwise fails + if selection.type_ == MenuSelectionType.Selection: + value = selection.value + + if self.auto_cursor: + cursor_pos = menu_options.index(value) + 1 # before the strip otherwise fails + + # in case the new position lands on a "placeholder" we'll skip them as well + while True: + if cursor_pos >= len(menu_options): + cursor_pos = 0 + if len(menu_options[cursor_pos]) > 0: + break + cursor_pos += 1 - # in case the new position lands on a "placeholder" we'll skip them as well - while True: - if cursor_pos >= len(menu_options): - cursor_pos = 0 - if len(menu_options[cursor_pos]) > 0: + value = value.strip() + + # if this calls returns false, we exit the menu + # we allow for an callback for special processing on realeasing control + if not self._process_selection(value): break - cursor_pos += 1 - selection = selection.strip() - if selection: - # if this calls returns false, we exit the menu. We allow for an callback for special processing on realeasing control - if not self._process_selection(selection): - break if not self.is_context_mgr: self.__exit__() @@ -443,15 +450,17 @@ class GeneralMenu: def mandatory_overview(self) -> Tuple[int, int]: mandatory_fields = 0 mandatory_waiting = 0 - for field in self._menu_options: - option = self._menu_options[field] + for field, option in self._menu_options.items(): if option.is_mandatory(): mandatory_fields += 1 if not option.has_selection(): mandatory_waiting += 1 return mandatory_fields, mandatory_waiting - def _select_archinstall_language(self, default_lang): - language = select_archinstall_language(default_lang) - self._translation.activate(language) - return language + def _select_archinstall_language(self, preset_value: str) -> str: + language = select_archinstall_language(preset_value) + if language is not None: + self._translation.activate(language) + return language + + return preset_value diff --git a/archinstall/lib/menu/simple_menu.py b/archinstall/lib/menu/simple_menu.py index a0a241bd..947259eb 100644 --- a/archinstall/lib/menu/simple_menu.py +++ b/archinstall/lib/menu/simple_menu.py @@ -596,7 +596,8 @@ class TerminalMenu: status_bar: Optional[Union[str, Iterable[str], Callable[[str], str]]] = None, status_bar_below_preview: bool = DEFAULT_STATUS_BAR_BELOW_PREVIEW, status_bar_style: Optional[Iterable[str]] = DEFAULT_STATUS_BAR_STYLE, - title: Optional[Union[str, Iterable[str]]] = None + title: Optional[Union[str, Iterable[str]]] = None, + explode_on_interrupt: bool = False ): def extract_shortcuts_menu_entries_and_preview_arguments( entries: Iterable[str], @@ -718,6 +719,7 @@ class TerminalMenu: self._search_case_sensitive = search_case_sensitive self._search_highlight_style = tuple(search_highlight_style) if search_highlight_style is not None else () self._search_key = search_key + self._explode_on_interrupt = explode_on_interrupt self._shortcut_brackets_highlight_style = ( tuple(shortcut_brackets_highlight_style) if shortcut_brackets_highlight_style is not None else () ) @@ -1538,7 +1540,9 @@ class TerminalMenu: # Only append `next_key` if it is a printable character and the first character is not the # `search_start` key self._search.search_text += next_key - except KeyboardInterrupt: + except KeyboardInterrupt as e: + if self._explode_on_interrupt: + raise e menu_was_interrupted = True finally: reset_signal_handling() @@ -1841,6 +1845,12 @@ def get_argumentparser() -> argparse.ArgumentParser: ), ) parser.add_argument("-t", "--title", action="store", dest="title", help="menu title") + parser.add_argument( + "--explode-on-interrupt", + action="store_true", + dest="explode_on_interrupt", + help="Instead of quitting the menu, this will raise the KeyboardInterrupt Exception", + ) parser.add_argument( "-V", "--version", action="store_true", dest="print_version", help="print the version number and exit" ) @@ -1971,6 +1981,7 @@ def main() -> None: status_bar_below_preview=args.status_bar_below_preview, status_bar_style=args.status_bar_style, title=args.title, + explode_on_interrupt=args.explode_on_interrupt, ) except (InvalidParameterCombinationError, InvalidStyleError, UnknownMenuEntryError) as e: print(str(e), file=sys.stderr) diff --git a/archinstall/lib/profiles.py b/archinstall/lib/profiles.py index 65a30b0b..33214ee8 100644 --- a/archinstall/lib/profiles.py +++ b/archinstall/lib/profiles.py @@ -207,6 +207,10 @@ class Profile(Script): def __repr__(self, *args :str, **kwargs :str) -> str: return f'Profile({os.path.basename(self.profile)})' + @property + def name(self) -> str: + return os.path.basename(self.profile) + def install(self) -> ModuleType: # Before installing, revert any temporary changes to the namespace. # This ensures that the namespace during installation is the original initiation namespace. diff --git a/archinstall/lib/user_interaction/disk_conf.py b/archinstall/lib/user_interaction/disk_conf.py index e84f647a..41657e1b 100644 --- a/archinstall/lib/user_interaction/disk_conf.py +++ b/archinstall/lib/user_interaction/disk_conf.py @@ -6,13 +6,14 @@ from .partitioning_conf import manage_new_and_existing_partitions, get_default_p from ..disk import BlockDevice from ..exceptions import DiskError from ..menu import Menu +from ..menu.menu import MenuSelectionType from ..output import log if TYPE_CHECKING: _: Any -def ask_for_main_filesystem_format(advanced_options=False): +def ask_for_main_filesystem_format(advanced_options=False) -> str: options = {'btrfs': 'btrfs', 'ext4': 'ext4', 'xfs': 'xfs', 'f2fs': 'f2fs'} advanced = {'ntfs': 'ntfs'} @@ -22,7 +23,7 @@ def ask_for_main_filesystem_format(advanced_options=False): prompt = _('Select which filesystem your main partition should use') choice = Menu(prompt, options, skip=False).run() - return choice + return choice.value def select_individual_blockdevice_usage(block_devices: list) -> Dict[str, Any]: @@ -30,24 +31,33 @@ def select_individual_blockdevice_usage(block_devices: list) -> Dict[str, Any]: for device in block_devices: layout = manage_new_and_existing_partitions(device) - result[device.path] = layout return result -def select_disk_layout(block_devices: list, advanced_options=False) -> Optional[Dict[str, Any]]: +def select_disk_layout(preset: Optional[Dict[str, Any]], block_devices: list, advanced_options=False) -> Optional[Dict[str, Any]]: wipe_mode = str(_('Wipe all selected drives and use a best-effort default partition layout')) custome_mode = str(_('Select what to do with each individual drive (followed by partition usage)')) modes = [wipe_mode, custome_mode] - mode = Menu(_('Select what you wish to do with the selected block devices'), modes).run() - - if mode: - if mode == wipe_mode: - return get_default_partition_layout(block_devices, advanced_options) - else: - return select_individual_blockdevice_usage(block_devices) + warning = str(_('Are you sure you want to reset this setting?')) + + choice = Menu( + _('Select what you wish to do with the selected block devices'), + modes, + explode_on_interrupt=True, + explode_warning=warning + ).run() + + match choice.type_: + case MenuSelectionType.Esc: return preset + case MenuSelectionType.Ctrl_c: return None + case MenuSelectionType.Selection: + if choice.value == wipe_mode: + return get_default_partition_layout(block_devices, advanced_options) + else: + return select_individual_blockdevice_usage(block_devices) def select_disk(dict_o_disks: Dict[str, BlockDevice]) -> BlockDevice: diff --git a/archinstall/lib/user_interaction/general_conf.py b/archinstall/lib/user_interaction/general_conf.py index 78f6b460..af996331 100644 --- a/archinstall/lib/user_interaction/general_conf.py +++ b/archinstall/lib/user_interaction/general_conf.py @@ -3,6 +3,9 @@ from __future__ import annotations import logging from typing import List, Any, Optional, Dict, TYPE_CHECKING +import archinstall + +from ..menu.menu import MenuSelectionType from ..menu.text_input import TextInput from ..locale_helpers import list_keyboard_languages, list_timezones @@ -26,7 +29,8 @@ def ask_ntp(preset: bool = True) -> bool: else: preset_val = Menu.no() choice = Menu(prompt, Menu.yes_no(), skip=False, preset_values=preset_val, default_option=Menu.yes()).run() - return False if choice == Menu.no() else True + + return False if choice.value == Menu.no() else True def ask_hostname(preset: str = None) -> str: @@ -38,23 +42,31 @@ def ask_for_a_timezone(preset: str = None) -> str: timezones = list_timezones() default = 'UTC' - selected_tz = Menu(_('Select a timezone'), - list(timezones), - skip=False, - preset_values=preset, - default_option=default).run() + choice = Menu( + _('Select a timezone'), + list(timezones), + preset_values=preset, + default_option=default + ).run() - return selected_tz + match choice.type_: + case MenuSelectionType.Esc: return preset + case MenuSelectionType.Selection: return choice.value def ask_for_audio_selection(desktop: bool = True, preset: str = None) -> str: - audio = 'pipewire' if desktop else 'none' - choices = ['pipewire', 'pulseaudio'] if desktop else ['pipewire', 'pulseaudio', 'none'] - selected_audio = Menu(_('Choose an audio server'), choices, preset_values=preset, default_option=audio, skip=False).run() - return selected_audio + no_audio = str(_('No audio server')) + choices = ['pipewire', 'pulseaudio'] if desktop else ['pipewire', 'pulseaudio', no_audio] + default = 'pipewire' if desktop else no_audio + + choice = Menu(_('Choose an audio server'), choices, preset_values=preset, default_option=default).run() + match choice.type_: + case MenuSelectionType.Esc: return preset + case MenuSelectionType.Selection: return choice.value -def select_language(default_value: str, preset_value: str = None) -> str: + +def select_language(preset_value: str = None) -> str: """ Asks the user to select a language Usually this is combined with :ref:`archinstall.list_keyboard_languages`. @@ -64,16 +76,19 @@ def select_language(default_value: str, preset_value: str = None) -> str: """ kb_lang = list_keyboard_languages() # sort alphabetically and then by length - # it's fine if the list is big because the Menu - # allows for searching anyways sorted_kb_lang = sorted(sorted(list(kb_lang)), key=len) - selected_lang = Menu(_('Select Keyboard layout'), - sorted_kb_lang, - default_option=default_value, - preset_values=preset_value, - sort=False).run() - return selected_lang + selected_lang = Menu( + _('Select Keyboard layout'), + sorted_kb_lang, + preset_values=preset_value, + sort=False + ).run() + + if selected_lang.value is None: + return preset_value + + return selected_lang.value def select_mirror_regions(preset_values: Dict[str, Any] = {}) -> Dict[str, Any]: @@ -89,15 +104,18 @@ def select_mirror_regions(preset_values: Dict[str, Any] = {}) -> Dict[str, Any]: else: preselected = list(preset_values.keys()) mirrors = list_mirrors() - selected_mirror = Menu(_('Select one of the regions to download packages from'), - list(mirrors.keys()), - preset_values=preselected, - multi=True).run() + selected_mirror = Menu( + _('Select one of the regions to download packages from'), + list(mirrors.keys()), + preset_values=preselected, + multi=True, + explode_on_interrupt=True + ).run() - if selected_mirror is not None: - return {selected: mirrors[selected] for selected in selected_mirror} - - return {} + match selected_mirror.type_: + case MenuSelectionType.Ctrl_c: return {} + case MenuSelectionType.Esc: return preset_values + case _: return {selected: mirrors[selected] for selected in selected_mirror.value} def select_archinstall_language(default='English'): @@ -106,7 +124,7 @@ def select_archinstall_language(default='English'): return language -def select_profile() -> Optional[Profile]: +def select_profile(preset) -> Optional[Profile]: """ # Asks the user to select a profile from the available profiles. # @@ -125,12 +143,27 @@ def select_profile() -> Optional[Profile]: title = _('This is a list of pre-programmed profiles, they might make it easier to install things like desktop environments') - selection = Menu(title=title, p_options=list(options.keys())).run() - - if selection is not None: - return options[selection] - - return None + warning = str(_('Are you sure you want to reset this setting?')) + + selection = Menu( + title=title, + p_options=list(options.keys()), + explode_on_interrupt=True, + explode_warning=warning + ).run() + + match selection.type_: + case MenuSelectionType.Selection: + return options[selection.value] if selection.value is not None else None + case MenuSelectionType.Ctrl_c: + archinstall.storage['profile_minimal'] = False + archinstall.storage['_selected_servers'] = [] + archinstall.storage['_desktop_profile'] = None + archinstall.arguments['desktop-environment'] = None + archinstall.arguments['gfx_driver_packages'] = None + return None + case MenuSelectionType.Esc: + return None def ask_additional_packages_to_install(pre_set_packages: List[str] = []) -> List[str]: @@ -171,14 +204,16 @@ def select_additional_repositories(preset: List[str]) -> List[str]: repositories = ["multilib", "testing"] - additional_repositories = Menu(_('Choose which optional additional repositories to enable'), - repositories, - sort=False, - multi=True, - preset_values=preset, - default_option=[]).run() - - if additional_repositories is not None: - return additional_repositories - - return [] + choice = Menu( + _('Choose which optional additional repositories to enable'), + repositories, + sort=False, + multi=True, + preset_values=preset, + explode_on_interrupt=True + ).run() + + match choice.type_: + case MenuSelectionType.Esc: return preset + case MenuSelectionType.Ctrl_c: return [] + case MenuSelectionType.Selection: return choice.value diff --git a/archinstall/lib/user_interaction/locale_conf.py b/archinstall/lib/user_interaction/locale_conf.py index d48018cf..15720064 100644 --- a/archinstall/lib/user_interaction/locale_conf.py +++ b/archinstall/lib/user_interaction/locale_conf.py @@ -4,32 +4,39 @@ from typing import Any, TYPE_CHECKING from ..locale_helpers import list_locales from ..menu import Menu +from ..menu.menu import MenuSelectionType if TYPE_CHECKING: _: Any -def select_locale_lang(default: str, preset: str = None) -> str: +def select_locale_lang(preset: str = None) -> str: locales = list_locales() locale_lang = set([locale.split()[0] for locale in locales]) - selected_locale = Menu(_('Choose which locale language to use'), - locale_lang, - sort=True, - preset_values=preset, - default_option=default).run() + selected_locale = Menu( + _('Choose which locale language to use'), + list(locale_lang), + sort=True, + preset_values=preset + ).run() - return selected_locale + match selected_locale.type_: + case MenuSelectionType.Selection: return selected_locale.value + case MenuSelectionType.Esc: return preset -def select_locale_enc(default: str, preset: str = None) -> str: +def select_locale_enc(preset: str = None) -> str: locales = list_locales() locale_enc = set([locale.split()[1] for locale in locales]) - selected_locale = Menu(_('Choose which locale encoding to use'), - locale_enc, - sort=True, - preset_values=preset, - default_option=default).run() + selected_locale = Menu( + _('Choose which locale encoding to use'), + list(locale_enc), + sort=True, + preset_values=preset + ).run() - return selected_locale + match selected_locale.type_: + case MenuSelectionType.Selection: return selected_locale.value + case MenuSelectionType.Esc: return preset diff --git a/archinstall/lib/user_interaction/network_conf.py b/archinstall/lib/user_interaction/network_conf.py index 80c9106b..e4e681ce 100644 --- a/archinstall/lib/user_interaction/network_conf.py +++ b/archinstall/lib/user_interaction/network_conf.py @@ -4,6 +4,7 @@ import ipaddress import logging from typing import Any, Optional, TYPE_CHECKING, List, Union +from ..menu.menu import MenuSelectionType from ..menu.text_input import TextInput from ..models.network_configuration import NetworkConfiguration, NicType @@ -66,8 +67,12 @@ class ManualNetworkConfig(ListManager): def _select_iface(self, existing_ifaces: List[str]) -> Optional[str]: all_ifaces = list_interfaces().values() available = set(all_ifaces) - set(existing_ifaces) - iface = Menu(str(_('Select interface to add')), list(available), skip=True).run() - return iface + choice = Menu(str(_('Select interface to add')), list(available), skip=True).run() + + if choice.type_ == MenuSelectionType.Esc: + return None + + return choice.value def _edit_iface(self, edit_iface :NetworkConfiguration): iface_name = edit_iface.iface @@ -75,9 +80,9 @@ class ManualNetworkConfig(ListManager): default_mode = 'DHCP (auto detect)' prompt = _('Select which mode to configure for "{}" or skip to use default mode "{}"').format(iface_name, default_mode) - mode = Menu(prompt, modes, default_option=default_mode).run() + mode = Menu(prompt, modes, default_option=default_mode, skip=False).run() - if mode == 'IP (static)': + if mode.value == 'IP (static)': while 1: prompt = _('Enter the IP and subnet for {} (example: 192.168.0.5/24): ').format(iface_name) ip = TextInput(prompt, edit_iface.ip).run().strip() @@ -107,6 +112,7 @@ class ManualNetworkConfig(ListManager): display_dns = None dns_input = TextInput(_('Enter your DNS servers (space separated, blank for none): '), display_dns).run().strip() + dns = [] if len(dns_input): dns = dns_input.split(' ') @@ -135,23 +141,28 @@ def ask_to_configure_network(preset: Union[None, NetworkConfiguration, List[Netw elif preset.type == 'network_manager': cursor_idx = 1 - nic = Menu(_( - 'Select one network interface to configure'), + warning = str(_('Are you sure you want to reset this setting?')) + + choice = Menu( + _('Select one network interface to configure'), list(network_options.values()), cursor_index=cursor_idx, - sort=False + sort=False, + explode_on_interrupt=True, + explode_warning=warning ).run() - if not nic: - return preset + match choice.type_: + case MenuSelectionType.Esc: return preset + case MenuSelectionType.Ctrl_c: return None - if nic == network_options['none']: + if choice.value == network_options['none']: return None - elif nic == network_options['iso_config']: + elif choice.value == network_options['iso_config']: return NetworkConfiguration(NicType.ISO) - elif nic == network_options['network_manager']: + elif choice.value == network_options['network_manager']: return NetworkConfiguration(NicType.NM) - elif nic == network_options['manual']: + elif choice.value == network_options['manual']: manual = ManualNetworkConfig('Configure interfaces', preset) return manual.run_manual() diff --git a/archinstall/lib/user_interaction/partitioning_conf.py b/archinstall/lib/user_interaction/partitioning_conf.py index afca1cef..c3dc4146 100644 --- a/archinstall/lib/user_interaction/partitioning_conf.py +++ b/archinstall/lib/user_interaction/partitioning_conf.py @@ -1,8 +1,10 @@ from __future__ import annotations +import copy from typing import List, Any, Dict, Union, TYPE_CHECKING, Callable, Optional from ..menu import Menu +from ..menu.menu import MenuSelectionType from ..output import log from ..disk.validators import fs_types @@ -80,21 +82,26 @@ def _get_partitions(partitions :List[Partition], filter_ :Callable = None) -> Li return partition_indexes -def select_partition(title :str, partitions :List[Partition], multiple :bool = False, filter_ :Callable = None) -> Union[int, List[int], None]: +def select_partition( + title :str, + partitions :List[Partition], + multiple :bool = False, + filter_ :Callable = None +) -> Optional[int, List[int]]: partition_indexes = _get_partitions(partitions, filter_) if len(partition_indexes) == 0: return None - partition = Menu(title, partition_indexes, multi=multiple).run() + choice = Menu(title, partition_indexes, multi=multiple).run() - if partition is not None: - if isinstance(partition, list): - return [int(p) for p in partition] - else: - return int(partition) + if choice.type_ == MenuSelectionType.Esc: + return None - return None + if isinstance(choice.value, list): + return [int(p) for p in choice.value] + else: + return int(choice.value) def get_default_partition_layout( @@ -114,14 +121,15 @@ def select_individual_blockdevice_usage(block_devices: list) -> Dict[str, Any]: for device in block_devices: layout = manage_new_and_existing_partitions(device) - result[device.path] = layout return result -def manage_new_and_existing_partitions(block_device: 'BlockDevice') -> Dict[str, Any]: # noqa: max-complexity: 50 +def manage_new_and_existing_partitions(block_device: 'BlockDevice') -> Dict[str, Any]: # noqa: max-complexity: 50 block_device_struct = {"partitions": [partition.__dump__() for partition in block_device.partitions.values()]} + original_layout = copy.deepcopy(block_device_struct) + # Test code: [part.__dump__() for part in block_device.partitions.values()] # TODO: Squeeze in BTRFS subvolumes here @@ -136,6 +144,8 @@ def manage_new_and_existing_partitions(block_device: 'BlockDevice') -> Dict[str, mark_bootable = str(_('Mark/Unmark a partition as bootable (automatic for /boot)')) set_filesystem_partition = str(_('Set desired filesystem for a partition')) set_btrfs_subvolumes = str(_('Set desired subvolumes on a btrfs partition')) + save_and_exit = str(_('Save and exit')) + cancel = str(_('Cancel')) while True: modes = [new_partition, suggest_partition_layout] @@ -166,11 +176,15 @@ def manage_new_and_existing_partitions(block_device: 'BlockDevice') -> Dict[str, if len(block_device_struct["partitions"]): title += _current_partition_layout(block_device_struct['partitions']) + '\n' - task = Menu(title, modes, sort=False).run() + modes += [save_and_exit, cancel] - if not task: - break + task = Menu(title, modes, sort=False, skip=False).run() + task = task.value + if task == cancel: + return original_layout + elif task == save_and_exit: + break if task == new_partition: from ..disk import valid_parted_position @@ -179,9 +193,9 @@ def manage_new_and_existing_partitions(block_device: 'BlockDevice') -> Dict[str, # # https://www.gnu.org/software/parted/manual/html_node/mklabel.html # name = input("Enter a desired name for the partition: ").strip() - fstype = Menu(_('Enter a desired filesystem type for the partition'), fs_types()).run() + fs_choice = Menu(_('Enter a desired filesystem type for the partition'), fs_types()).run() - if not fstype: + if fs_choice.type_ == MenuSelectionType.Esc: continue prompt = _('Enter the start sector (percentage or block number, default: {}): ').format( @@ -214,7 +228,7 @@ def manage_new_and_existing_partitions(block_device: 'BlockDevice') -> Dict[str, "mountpoint": None, "wipe": True, "filesystem": { - "format": fstype + "format": fs_choice.value } }) else: @@ -225,16 +239,13 @@ def manage_new_and_existing_partitions(block_device: 'BlockDevice') -> Dict[str, from ..disk import suggest_single_disk_layout if len(block_device_struct["partitions"]): - prompt = _('{} contains queued partitions, this will remove those, are you sure?').format(block_device) - choice = Menu(prompt, Menu.yes_no(), default_option=Menu.no()).run() + prompt = _('{}\ncontains queued partitions, this will remove those, are you sure?').format(block_device) + choice = Menu(prompt, Menu.yes_no(), default_option=Menu.no(), skip=False).run() - if choice == Menu.no(): + if choice.value == Menu.no(): continue block_device_struct.update(suggest_single_disk_layout(block_device)[block_device.path]) - - elif task is None: - return block_device_struct else: current_layout = _current_partition_layout(block_device_struct['partitions'], with_idx=True) @@ -265,10 +276,8 @@ def manage_new_and_existing_partitions(block_device: 'BlockDevice') -> Dict[str, partition = select_partition(title, block_device_struct["partitions"]) if partition is not None: - print( - _(' * Partition mount-points are relative to inside the installation, the boot would be /boot as an example.')) - mountpoint = input( - _('Select where to mount partition (leave blank to remove mountpoint): ')).strip() + print(_(' * Partition mount-points are relative to inside the installation, the boot would be /boot as an example.')) + mountpoint = input(_('Select where to mount partition (leave blank to remove mountpoint): ')).strip() if len(mountpoint): block_device_struct["partitions"][partition]['mountpoint'] = mountpoint @@ -290,10 +299,10 @@ def manage_new_and_existing_partitions(block_device: 'BlockDevice') -> Dict[str, if not block_device_struct["partitions"][partition].get('filesystem', None): block_device_struct["partitions"][partition]['filesystem'] = {} - fstype = Menu(_('Enter a desired filesystem type for the partition'), fs_types()).run() + fs_choice = Menu(_('Enter a desired filesystem type for the partition'), fs_types()).run() - if fstype: - block_device_struct["partitions"][partition]['filesystem']['format'] = fstype + if fs_choice.type_ == MenuSelectionType.Selection: + block_device_struct["partitions"][partition]['filesystem']['format'] = fs_choice.value # Negate the current wipe marking block_device_struct["partitions"][partition]['wipe'] = not block_device_struct["partitions"][partition].get('wipe', False) @@ -304,16 +313,16 @@ def manage_new_and_existing_partitions(block_device: 'BlockDevice') -> Dict[str, if partition is not None: # Negate the current encryption marking - block_device_struct["partitions"][partition][ - 'encrypted'] = not block_device_struct["partitions"][partition].get('encrypted', False) + block_device_struct["partitions"][partition]['encrypted'] = \ + not block_device_struct["partitions"][partition].get('encrypted', False) elif task == mark_bootable: title = _('{}\n\nSelect which partition to mark as bootable').format(current_layout) partition = select_partition(title, block_device_struct["partitions"]) if partition is not None: - block_device_struct["partitions"][partition][ - 'boot'] = not block_device_struct["partitions"][partition].get('boot', False) + block_device_struct["partitions"][partition]['boot'] = \ + not block_device_struct["partitions"][partition].get('boot', False) elif task == set_filesystem_partition: title = _('{}\n\nSelect which partition to set a filesystem on').format(current_layout) @@ -324,10 +333,10 @@ def manage_new_and_existing_partitions(block_device: 'BlockDevice') -> Dict[str, block_device_struct["partitions"][partition]['filesystem'] = {} fstype_title = _('Enter a desired filesystem type for the partition: ') - fstype = Menu(fstype_title, fs_types()).run() + fs_choice = Menu(fstype_title, fs_types()).run() - if fstype: - block_device_struct["partitions"][partition]['filesystem']['format'] = fstype + if fs_choice.type_ == MenuSelectionType.Selection: + block_device_struct["partitions"][partition]['filesystem']['format'] = fs_choice.value elif task == set_btrfs_subvolumes: from .subvolume_config import SubvolumeList diff --git a/archinstall/lib/user_interaction/save_conf.py b/archinstall/lib/user_interaction/save_conf.py index c52b97e2..f542bc9b 100644 --- a/archinstall/lib/user_interaction/save_conf.py +++ b/archinstall/lib/user_interaction/save_conf.py @@ -5,6 +5,7 @@ from typing import Any, Dict, TYPE_CHECKING from ..configuration import ConfigurationOutput from ..menu import Menu +from ..menu.menu import MenuSelectionType from ..output import log if TYPE_CHECKING: @@ -45,14 +46,16 @@ def save_config(config: Dict): 'all': str(_('Save all')) } - selection = Menu(_('Choose which configuration to save'), - list(options.values()), - sort=False, - skip=True, - preview_size=0.75, - preview_command=preview).run() + choice = Menu( + _('Choose which configuration to save'), + list(options.values()), + sort=False, + skip=True, + preview_size=0.75, + preview_command=preview + ).run() - if not selection: + if choice.type_ == MenuSelectionType.Esc: return while True: @@ -62,13 +65,13 @@ def save_config(config: Dict): break log(_('Not a valid directory: {}').format(dest_path), fg='red') - if options['user_config'] == selection: + if options['user_config'] == choice.value: config_output.save_user_config(dest_path) - elif options['user_creds'] == selection: + elif options['user_creds'] == choice.value: config_output.save_user_creds(dest_path) - elif options['disk_layout'] == selection: + elif options['disk_layout'] == choice.value: config_output.save_disk_layout(dest_path) - elif options['all'] == selection: + elif options['all'] == choice.value: config_output.save_user_config(dest_path) config_output.save_user_creds(dest_path) - config_output.save_disk_layout + config_output.save_disk_layout(dest_path) diff --git a/archinstall/lib/user_interaction/system_conf.py b/archinstall/lib/user_interaction/system_conf.py index 17c093c1..f4ada14b 100644 --- a/archinstall/lib/user_interaction/system_conf.py +++ b/archinstall/lib/user_interaction/system_conf.py @@ -6,10 +6,9 @@ from ..disk import all_blockdevices from ..exceptions import RequirementError from ..hardware import AVAILABLE_GFX_DRIVERS, has_uefi, has_amd_graphics, has_intel_graphics, has_nvidia_graphics from ..menu import Menu +from ..menu.menu import MenuSelectionType from ..storage import storage -from ..translation import DeferredTranslation - if TYPE_CHECKING: _: Any @@ -25,13 +24,22 @@ def select_kernel(preset: List[str] = None) -> List[str]: kernels = ["linux", "linux-lts", "linux-zen", "linux-hardened"] default_kernel = "linux" - selected_kernels = Menu(_('Choose which kernels to use or leave blank for default "{}"').format(default_kernel), - kernels, - sort=True, - multi=True, - preset_values=preset, - default_option=default_kernel).run() - return selected_kernels + warning = str(_('Are you sure you want to reset this setting?')) + + choice = Menu( + _('Choose which kernels to use or leave blank for default "{}"').format(default_kernel), + kernels, + sort=True, + multi=True, + preset_values=preset, + explode_on_interrupt=True, + explode_warning=warning + ).run() + + match choice.type_: + case MenuSelectionType.Esc: return preset + case MenuSelectionType.Ctrl_c: return [] + case MenuSelectionType.Selection: return choice.value def select_harddrives(preset: List[str] = []) -> List[str]: @@ -49,15 +57,24 @@ def select_harddrives(preset: List[str] = []) -> List[str]: else: preset_disks = {} - selected_harddrive = Menu(_('Select one or more hard drives to use and configure'), - list(options.keys()), - preset_values=list(preset_disks.keys()), - multi=True).run() + title = str(_('Select one or more hard drives to use and configure\n')) + title += str(_('Any modifications to the existing setting will reset the disk layout!')) - if selected_harddrive and len(selected_harddrive) > 0: - return [options[i] for i in selected_harddrive] + warning = str(_('If you reset the harddrive selection this will also reset the current disk layout. Are you sure?')) - return [] + selected_harddrive = Menu( + title, + list(options.keys()), + preset_values=list(preset_disks.keys()), + multi=True, + explode_on_interrupt=True, + explode_warning=warning + ).run() + + match selected_harddrive.type_: + case MenuSelectionType.Ctrl_c: return [] + case MenuSelectionType.Esc: return preset + case MenuSelectionType.Selection: return [options[i] for i in selected_harddrive.value] def select_driver(options: Dict[str, Any] = AVAILABLE_GFX_DRIVERS) -> str: @@ -73,34 +90,34 @@ def select_driver(options: Dict[str, Any] = AVAILABLE_GFX_DRIVERS) -> str: if drivers: arguments = storage.get('arguments', {}) - title = DeferredTranslation('') + title = '' if has_amd_graphics(): - title += _( + title += str(_( 'For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options.' - ) + '\n' + )) + '\n' if has_intel_graphics(): - title += _( + title += str(_( 'For the best compatibility with your Intel hardware, you may want to use either the all open-source or Intel options.\n' - ) + )) if has_nvidia_graphics(): - title += _( + title += str(_( 'For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n' - ) + )) - title += _('\n\nSelect a graphics driver or leave blank to install all open-source drivers') - arguments['gfx_driver'] = Menu(title, drivers).run() + title += str(_('\n\nSelect a graphics driver or leave blank to install all open-source drivers')) + choice = Menu(title, drivers).run() - if arguments.get('gfx_driver', None) is None: - arguments['gfx_driver'] = _("All open-source (default)") + if choice.type_ != MenuSelectionType.Selection: + return arguments.get('gfx_driver') - return options.get(arguments.get('gfx_driver')) + arguments['gfx_driver'] = choice.value + return options.get(choice.value) raise RequirementError("Selecting drivers require a least one profile to be given as an option.") def ask_for_bootloader(advanced_options: bool = False, preset: str = None) -> str: - if preset == 'systemd-bootctl': preset_val = 'systemd-boot' if advanced_options else Menu.no() elif preset == 'grub-install': @@ -109,26 +126,36 @@ def ask_for_bootloader(advanced_options: bool = False, preset: str = None) -> st preset_val = preset bootloader = "systemd-bootctl" if has_uefi() else "grub-install" + if has_uefi(): if not advanced_options: - bootloader_choice = Menu(_('Would you like to use GRUB as a bootloader instead of systemd-boot?'), - Menu.yes_no(), - preset_values=preset_val, - default_option=Menu.no()).run() - - if bootloader_choice == Menu.yes(): - bootloader = "grub-install" + selection = Menu( + _('Would you like to use GRUB as a bootloader instead of systemd-boot?'), + Menu.yes_no(), + preset_values=preset_val, + default_option=Menu.no() + ).run() + + match selection.type_: + case MenuSelectionType.Esc: return preset + case MenuSelectionType.Selection: bootloader = 'grub-install' if selection.value == Menu.yes() else bootloader else: # We use the common names for the bootloader as the selection, and map it back to the expected values. choices = ['systemd-boot', 'grub', 'efistub'] selection = Menu(_('Choose a bootloader'), choices, preset_values=preset_val).run() - if selection != "": - if selection == 'systemd-boot': + + value = '' + match selection.type_: + case MenuSelectionType.Esc: value = preset_val + case MenuSelectionType.Selection: value = selection.value + + if value != "": + if value == 'systemd-boot': bootloader = 'systemd-bootctl' - elif selection == 'grub': + elif value == 'grub': bootloader = 'grub-install' else: - bootloader = selection + bootloader = value return bootloader @@ -138,6 +165,10 @@ def ask_for_swap(preset: bool = True) -> bool: preset_val = Menu.yes() else: preset_val = Menu.no() + prompt = _('Would you like to use swap on zram?') choice = Menu(prompt, Menu.yes_no(), default_option=Menu.yes(), preset_values=preset_val).run() - return False if choice == Menu.no() else True + + match choice.type_: + case MenuSelectionType.Esc: return preset + case MenuSelectionType.Selection: return False if choice.value == Menu.no() else True diff --git a/archinstall/lib/user_interaction/utils.py b/archinstall/lib/user_interaction/utils.py index 59f2dfbc..ce48607d 100644 --- a/archinstall/lib/user_interaction/utils.py +++ b/archinstall/lib/user_interaction/utils.py @@ -30,7 +30,7 @@ def check_password_strong(passwd: str) -> bool: if symbol_count**len(passwd) < 10e20: prompt = str(_("The password you are using seems to be weak, are you sure you want to use it?")) choice = Menu(prompt, Menu.yes_no(), default_option=Menu.yes()).run() - return choice == Menu.yes() + return choice.value == Menu.yes() return True @@ -40,7 +40,6 @@ def get_password(prompt: str = '') -> Optional[str]: prompt = _("Enter a password: ") while passwd := getpass.getpass(prompt): - if len(passwd.strip()) <= 0: break @@ -82,11 +81,12 @@ def do_countdown() -> bool: if SIG_TRIGGER: prompt = _('Do you really want to abort?') choice = Menu(prompt, Menu.yes_no(), skip=False).run() - if choice == 'yes': + if choice.value == Menu.yes(): exit(0) if SIG_TRIGGER is False: sys.stdin.read() + SIG_TRIGGER = False signal.signal(signal.SIGINT, sig_handler) diff --git a/examples/guided.py b/examples/guided.py index b653ea2a..f104b7e3 100644 --- a/examples/guided.py +++ b/examples/guided.py @@ -45,9 +45,8 @@ def ask_user_questions(): # Set which region to download packages from during the installation global_menu.enable('mirror-region') - if archinstall.arguments.get('advanced', False): - global_menu.enable('sys-language', True) - global_menu.enable('sys-encoding', True) + global_menu.enable('sys-language') + global_menu.enable('sys-encoding') # Ask which harddrives/block-devices we will install to # and convert them into archinstall.BlockDevice() objects. diff --git a/examples/swiss.py b/examples/swiss.py index 6d357191..9c0d469a 100644 --- a/examples/swiss.py +++ b/examples/swiss.py @@ -161,7 +161,7 @@ class SetupMenu(archinstall.GeneralMenu): self.set_option('archinstall-language', archinstall.Selector( _('Select Archinstall language'), - lambda x: self._select_archinstall_language('English'), + lambda x: self._select_archinstall_language(x), default='English', enabled=True)) self.set_option('ntp', diff --git a/profiles/desktop.py b/profiles/desktop.py index eaece9f5..e94d3505 100644 --- a/profiles/desktop.py +++ b/profiles/desktop.py @@ -1,6 +1,12 @@ # A desktop environment selector. +from typing import Any, TYPE_CHECKING + import archinstall -from archinstall import log +from archinstall import log, Menu +from archinstall.lib.menu.menu import MenuSelectionType + +if TYPE_CHECKING: + _: Any is_top_level_profile = True @@ -46,23 +52,26 @@ def _prep_function(*args, **kwargs) -> bool: other code in this stage. So it's a safe way to ask the user for more input before any other installer steps start. """ - desktop = archinstall.Menu(str(_('Select your desired desktop environment')), __supported__).run() + choice = Menu(str(_('Select your desired desktop environment')), __supported__).run() + + if choice.type_ != MenuSelectionType.Selection: + return False - if desktop: + if choice.value: # Temporarily store the selected desktop profile # in a session-safe location, since this module will get reloaded # the next time it gets executed. if not archinstall.storage.get('_desktop_profile', None): - archinstall.storage['_desktop_profile'] = desktop + archinstall.storage['_desktop_profile'] = choice.value if not archinstall.arguments.get('desktop-environment', None): - archinstall.arguments['desktop-environment'] = desktop - profile = archinstall.Profile(None, desktop) + archinstall.arguments['desktop-environment'] = choice.value + profile = archinstall.Profile(None, choice.value) # Loading the instructions with a custom namespace, ensures that a __name__ comparison is never triggered. - with profile.load_instructions(namespace=f"{desktop}.py") as imported: + with profile.load_instructions(namespace=f"{choice.value}.py") as imported: if hasattr(imported, '_prep_function'): return imported._prep_function() else: - log(f"Deprecated (??): {desktop} profile has no _prep_function() anymore") + log(f"Deprecated (??): {choice.value} profile has no _prep_function() anymore") exit(1) return False diff --git a/profiles/i3.py b/profiles/i3.py index 3283848e..37029a02 100644 --- a/profiles/i3.py +++ b/profiles/i3.py @@ -1,6 +1,8 @@ # Common package for i3, lets user select which i3 configuration they want. import archinstall +from archinstall import Menu +from archinstall.lib.menu.menu import MenuSelectionType is_top_level_profile = False @@ -27,13 +29,16 @@ def _prep_function(*args, **kwargs): supported_configurations = ['i3-wm', 'i3-gaps'] - desktop = archinstall.Menu('Select your desired configuration', supported_configurations).run() + choice = Menu('Select your desired configuration', supported_configurations).run() - if desktop: + if choice.type_ != MenuSelectionType.Selection: + return False + + if choice.value: # Temporarily store the selected desktop profile # in a session-safe location, since this module will get reloaded # the next time it gets executed. - archinstall.storage['_i3_configuration'] = desktop + archinstall.storage['_i3_configuration'] = choice.value # i3 requires a functioning Xorg installation. profile = archinstall.Profile(None, 'xorg') @@ -43,6 +48,8 @@ def _prep_function(*args, **kwargs): else: print('Deprecated (??): xorg profile has no _prep_function() anymore') + return False + if __name__ == 'i3': """ diff --git a/profiles/minimal.py b/profiles/minimal.py index 3b161511..a412aa81 100644 --- a/profiles/minimal.py +++ b/profiles/minimal.py @@ -1,4 +1,5 @@ # Used to do a minimal install +import archinstall is_top_level_profile = True @@ -12,6 +13,7 @@ def _prep_function(*args, **kwargs): we don't need to do anything special here, but it needs to exist and return True. """ + archinstall.storage['profile_minimal'] = True return True # Do nothing and just return True diff --git a/profiles/server.py b/profiles/server.py index 91ac7cf2..21681c2f 100644 --- a/profiles/server.py +++ b/profiles/server.py @@ -1,8 +1,14 @@ # Used to select various server application profiles on top of a minimal installation. import logging +from typing import Any, TYPE_CHECKING import archinstall +from archinstall import Menu +from archinstall.lib.menu.menu import MenuSelectionType + +if TYPE_CHECKING: + _: Any is_top_level_profile = True @@ -26,15 +32,18 @@ def _prep_function(*args, **kwargs): Magic function called by the importing installer before continuing any further. """ - servers = archinstall.Menu(str(_( + choice = Menu(str(_( 'Choose which servers to install, if none then a minimal installation wil be done')), available_servers, - preset_values=archinstall.storage.get('_selected_servers', []), + preset_values=kwargs['servers'], multi=True ).run() - if servers: - archinstall.storage['_selected_servers'] = servers + if choice.type_ != MenuSelectionType.Selection: + return False + + if choice.value: + archinstall.storage['_selected_servers'] = choice.value return True return False diff --git a/profiles/sway.py b/profiles/sway.py index 0819db95..b7266da3 100644 --- a/profiles/sway.py +++ b/profiles/sway.py @@ -23,8 +23,9 @@ def _check_driver() -> bool: if packages and "nvidia" in packages: prompt = 'The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, are you okay with that?' - choice = Menu(prompt, Menu.yes_no(), default_option=Menu.no()).run() - if choice == Menu.no(): + choice = Menu(prompt, Menu.yes_no(), default_option=Menu.no(), skip=False).run() + + if choice.value == Menu.no(): return False return True -- cgit v1.2.3-70-g09d2 From 0c7506fe1c8b5a446a3dd4c9936d34806e439be6 Mon Sep 17 00:00:00 2001 From: edl2 <95947898+edl2@users.noreply.github.com> Date: Thu, 26 May 2022 03:11:38 -0400 Subject: Remove unneeded packages from awesome profile? (#1247) This profile currently installs a nemo - file manager, gpicview - image viewer, and maim - screenshot taker. all of these items do not typically come with awesome and aren't awesome specific and should be installed by the user. (also not required for the system to work). Especially since these are not even the most popular tools for each use. --- profiles/awesome.py | 3 --- 1 file changed, 3 deletions(-) (limited to 'profiles') diff --git a/profiles/awesome.py b/profiles/awesome.py index 9648fc4a..4593261b 100644 --- a/profiles/awesome.py +++ b/profiles/awesome.py @@ -7,9 +7,6 @@ is_top_level_profile = False # New way of defining packages for a profile, which is iterable and can be used out side # of the profile to get a list of "what packages will be installed". __packages__ = [ - "nemo", - "gpicview", - "maim", "alacritty", ] -- cgit v1.2.3-70-g09d2 From d38943bbadad998819735d45c1889aad5b554da5 Mon Sep 17 00:00:00 2001 From: edl2 <95947898+edl2@users.noreply.github.com> Date: Thu, 26 May 2022 03:14:21 -0400 Subject: Dmenu not needed (#1243) Qtile comes with simple run prompt already, want to keep as vanilla as possible. --- profiles/qtile.py | 1 - 1 file changed, 1 deletion(-) (limited to 'profiles') diff --git a/profiles/qtile.py b/profiles/qtile.py index ae1409a6..ace13dcc 100644 --- a/profiles/qtile.py +++ b/profiles/qtile.py @@ -11,7 +11,6 @@ __packages__ = [ 'alacritty', 'lightdm-gtk-greeter', 'lightdm', - 'dmenu' ] def _prep_function(*args, **kwargs): -- cgit v1.2.3-70-g09d2 From 870da403e79ab50350803b45f200e0b272334989 Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Fri, 27 May 2022 05:48:29 +1000 Subject: Rework user management (#1220) * Rework users * Update user installation * Fix config serialization * Update * Update schemas and documentation * Update * Fix flake8 * Make users mypy compatible * Fix minor copy Co-authored-by: Daniel Girtler Co-authored-by: Anton Hvornum --- .github/workflows/mypy.yaml | 2 +- README.md | 3 +- archinstall/__init__.py | 11 +- archinstall/lib/configuration.py | 2 +- archinstall/lib/installer.py | 16 +- archinstall/lib/menu/global_menu.py | 53 +++--- archinstall/lib/menu/list_manager.py | 37 ++--- archinstall/lib/models/users.py | 77 +++++++++ archinstall/lib/output.py | 38 ++++- archinstall/lib/user_interaction/__init__.py | 2 +- .../lib/user_interaction/manage_users_conf.py | 177 +++++++-------------- archinstall/lib/user_interaction/network_conf.py | 2 + .../lib/user_interaction/subvolume_config.py | 2 + docs/installing/guided.rst | 25 ++- examples/creds-sample.json | 20 ++- examples/guided.py | 10 +- examples/minimal.py | 6 +- examples/swiss.py | 19 ++- profiles/52-54-00-12-34-56.py | 3 +- schema.json | 10 +- 20 files changed, 290 insertions(+), 225 deletions(-) create mode 100644 archinstall/lib/models/users.py (limited to 'profiles') diff --git a/.github/workflows/mypy.yaml b/.github/workflows/mypy.yaml index 18c33e67..d14d8553 100644 --- a/.github/workflows/mypy.yaml +++ b/.github/workflows/mypy.yaml @@ -15,4 +15,4 @@ jobs: # one day this will be enabled # run: mypy --strict --module archinstall || exit 0 - name: run mypy - run: mypy --follow-imports=silent archinstall/lib/menu/selection_menu.py archinstall/lib/menu/global_menu.py archinstall/lib/models/network_configuration.py archinstall/lib/menu/list_manager.py archinstall/lib/user_interaction/network_conf.py + run: mypy --follow-imports=silent archinstall/lib/menu/selection_menu.py archinstall/lib/menu/global_menu.py archinstall/lib/models/network_configuration.py archinstall/lib/menu/list_manager.py archinstall/lib/user_interaction/network_conf.py archinstall/lib/models/users.py diff --git a/README.md b/README.md index 05a3c411..79fae095 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,8 @@ with archinstall.Installer('/mnt') as installation: # In this case, we install a minimal profile that is empty installation.install_profile('minimal') - installation.user_create('devel', 'devel') + user = User('devel', 'devel', False) + installation.create_users(user) installation.user_set_pw('root', 'airoot') ``` diff --git a/archinstall/__init__.py b/archinstall/__init__.py index fcb741e6..1e72a60e 100644 --- a/archinstall/__init__.py +++ b/archinstall/__init__.py @@ -1,7 +1,4 @@ """Arch Linux installer - guided, templates etc.""" -import urllib.error -import urllib.parse -import urllib.request from argparse import ArgumentParser from .lib.disk import * @@ -13,6 +10,7 @@ from .lib.locale_helpers import * from .lib.luks import * from .lib.mirrors import * from .lib.models.network_configuration import NetworkConfigurationHandler +from .lib.models.users import User from .lib.networking import * from .lib.output import * from .lib.models.dataclasses import ( @@ -160,7 +158,7 @@ def get_arguments() -> Dict[str, Any]: if args.creds is not None: if not json_stream_to_structure('--creds', args.creds, config): exit(1) - + # load the parameters. first the known, then the unknowns config.update(vars(args)) config.update(parse_unspecified_argument_list(unknowns)) @@ -211,6 +209,11 @@ def load_config(): handler = NetworkConfigurationHandler() handler.parse_arguments(arguments.get('nic')) arguments['nic'] = handler.configuration + if arguments.get('!users', None) is not None or arguments.get('!superusers', None) is not None: + users = arguments.get('!users', None) + superusers = arguments.get('!superusers', None) + arguments['!users'] = User.parse_arguments(users, superusers) + def post_process_arguments(arguments): storage['arguments'] = arguments diff --git a/archinstall/lib/configuration.py b/archinstall/lib/configuration.py index f3fe1e1c..510f7103 100644 --- a/archinstall/lib/configuration.py +++ b/archinstall/lib/configuration.py @@ -37,7 +37,7 @@ class ConfigurationOutput: self._user_creds_file = "user_credentials.json" self._disk_layout_file = "user_disk_layout.json" - self._sensitive = ['!users', '!superusers', '!encryption-password'] + self._sensitive = ['!users', '!encryption-password'] self._ignore = ['abort', 'install', 'config', 'creds', 'dry_run'] self._process_config() diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 5cedc9d6..903d33af 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -23,6 +23,7 @@ from .profiles import Profile from .disk.partition import get_mount_fs_type from .exceptions import DiskError, ServiceException, RequirementError, HardwareIncompatibilityError, SysCallError from .hsm import fido2_enroll +from .models.users import User if TYPE_CHECKING: _: Any @@ -251,7 +252,7 @@ class Installer: loopdev = f"{storage.get('ENC_IDENTIFIER', 'ai')}{pathlib.Path(partition['mountpoint']).name}loop" else: loopdev = f"{storage.get('ENC_IDENTIFIER', 'ai')}{pathlib.Path(partition['device_instance'].path).name}" - + # note that we DON'T auto_unmount (i.e. close the encrypted device so it can be used with (luks_handle := luks2(partition['device_instance'], loopdev, password, auto_unmount=False)) as unlocked_device: if partition.get('generate-encryption-key-file',False) and not self._has_root(partition): @@ -426,7 +427,7 @@ class Installer: if storage['arguments'].get('silent', False) is False: if input('Would you like to re-try this download? (Y/n): ').lower().strip() in ('', 'y'): return self.pacstrap(*packages, **kwargs) - + raise RequirementError("Pacstrap failed. See /var/log/archinstall/install.log or above message for error details.") def set_mirrors(self, mirrors :Mapping[str, Iterator[str]]) -> None: @@ -1062,7 +1063,7 @@ class Installer: self.log(f'Installing archinstall profile {profile}', level=logging.INFO) return profile.install() - def enable_sudo(self, entity: str, group :bool = False) -> bool: + def enable_sudo(self, entity: str, group :bool = False): self.log(f'Enabling sudo permissions for {entity}.', level=logging.INFO) sudoers_dir = f"{self.target}/etc/sudoers.d" @@ -1092,9 +1093,14 @@ class Installer: # Guarantees sudoer conf file recommended perms os.chmod(pathlib.Path(rule_file_name), 0o440) - return True + def create_users(self, users: Union[User, List[User]]): + if not isinstance(users, list): + users = [users] + + for user in users: + self.user_create(user.username, user.password, user.groups, user.sudo) - def user_create(self, user :str, password :Optional[str] = None, groups :Optional[str] = None, sudo :bool = False) -> None: + def user_create(self, user :str, password :Optional[str] = None, groups :Optional[List[str]] = None, sudo :bool = False) -> None: if groups is None: groups = [] diff --git a/archinstall/lib/menu/global_menu.py b/archinstall/lib/menu/global_menu.py index 53e0941d..5cb27cab 100644 --- a/archinstall/lib/menu/global_menu.py +++ b/archinstall/lib/menu/global_menu.py @@ -21,7 +21,6 @@ from ..user_interaction import ask_hostname from ..user_interaction import ask_for_audio_selection from ..user_interaction import ask_additional_packages_to_install from ..user_interaction import ask_to_configure_network -from ..user_interaction import ask_for_superuser_account from ..user_interaction import ask_for_additional_users from ..user_interaction import select_language from ..user_interaction import select_mirror_regions @@ -33,7 +32,9 @@ from ..user_interaction import select_encrypted_partitions from ..user_interaction import select_harddrives from ..user_interaction import select_profile from ..user_interaction import select_additional_repositories +from ..models.users import User from ..user_interaction.partitioning_conf import current_partition_layout +from ..output import FormattedOutput if TYPE_CHECKING: _: Any @@ -122,21 +123,13 @@ class GlobalMenu(GeneralMenu): _('Root password'), lambda preset:self._set_root_password(), display_func=lambda x: secret(x) if x else 'None') - self._menu_options['!superusers'] = \ - Selector( - _('Superuser account'), - lambda preset: self._create_superuser_account(), - default={}, - exec_func=lambda n,v:self._users_resynch(), - dependencies_not=['!root-password'], - display_func=lambda x: self._display_superusers()) self._menu_options['!users'] = \ Selector( _('User account'), - lambda x: self._create_user_account(), + lambda x: self._create_user_account(x), default={}, - exec_func=lambda n,v:self._users_resynch(), - display_func=lambda x: list(x.keys()) if x else '[]') + display_func=lambda x: f'{len(x)} {_("User(s)")}' if len(x) > 0 else None, + preview_func=self._prev_users) self._menu_options['profile'] = \ Selector( _('Profile'), @@ -273,17 +266,28 @@ class GlobalMenu(GeneralMenu): return text[:-1] # remove last new line return None + def _prev_users(self) -> Optional[str]: + selector = self._menu_options['!users'] + if selector.has_selection(): + users: List[User] = selector.current_selection + return FormattedOutput.as_table(users) + return None + def _missing_configs(self) -> List[str]: def check(s): return self._menu_options.get(s).has_selection() + def has_superuser() -> bool: + users = self._menu_options['!users'].current_selection + return any([u.sudo for u in users]) + missing = [] if not check('bootloader'): missing += ['Bootloader'] if not check('hostname'): missing += ['Hostname'] - if not check('!root-password') and not check('!superusers'): - missing += [str(_('Either root-password or at least 1 superuser must be specified'))] + if not check('!root-password') and not has_superuser(): + missing += [str(_('Either root-password or at least 1 user with sudo privileges must be specified'))] if not check('harddrives'): missing += ['Hard drives'] if check('harddrives'): @@ -380,23 +384,6 @@ class GlobalMenu(GeneralMenu): return ret - def _create_superuser_account(self) -> Optional[Dict[str, Dict[str, str]]]: - superusers = ask_for_superuser_account(str(_('Manage superuser accounts: '))) - return superusers if superusers else None - - def _create_user_account(self) -> Dict[str, Dict[str, str | None]]: - users = ask_for_additional_users(str(_('Manage ordinary user accounts: '))) + def _create_user_account(self, defined_users: List[User]) -> List[User]: + users = ask_for_additional_users(defined_users=defined_users) return users - - def _display_superusers(self): - superusers = self._data_store.get('!superusers', {}) - - if self._menu_options.get('!root-password').has_selection(): - return list(superusers.keys()) if superusers else '[]' - else: - return list(superusers.keys()) if superusers else '' - - def _users_resynch(self) -> bool: - self.synch('!superusers') - self.synch('!users') - return False diff --git a/archinstall/lib/menu/list_manager.py b/archinstall/lib/menu/list_manager.py index 7db3b3a9..cb567093 100644 --- a/archinstall/lib/menu/list_manager.py +++ b/archinstall/lib/menu/list_manager.py @@ -84,13 +84,13 @@ The contents in the base class of this methods serve for a very basic usage, and ``` """ - -from .text_input import TextInput -from .menu import Menu, MenuSelectionType +import copy from os import system -from copy import copy from typing import Union, Any, TYPE_CHECKING, Dict, Optional +from .text_input import TextInput +from .menu import Menu + if TYPE_CHECKING: _: Any @@ -144,14 +144,14 @@ class ListManager: self.bottom_list = [self.confirm_action,self.cancel_action] self.bottom_item = [self.cancel_action] self.base_actions = base_actions if base_actions else [str(_('Add')),str(_('Copy')),str(_('Edit')),str(_('Delete'))] - self.base_data = base_list - self._data = copy(base_list) # as refs, changes are immediate + self._original_data = copy.deepcopy(base_list) + self._data = copy.deepcopy(base_list) # as refs, changes are immediate # default values for the null case self.target: Optional[Any] = None self.action = self._null_action if len(self._data) == 0 and self._null_action: - self.exec_action(self._data) + self._data = self.exec_action(self._data) def run(self): while True: @@ -175,12 +175,10 @@ class ListManager: clear_screen=False, clear_menu_on_exit=False, header=self.header, - skip_empty_entries=True + skip_empty_entries=True, + skip=False ).run() - if target.type_ == MenuSelectionType.Esc: - return self.run() - if not target.value or target.value in self.bottom_list: self.action = target break @@ -188,21 +186,23 @@ class ListManager: if target.value and target.value in self._default_action: self.action = target.value self.target = None - self.exec_action(self._data) + self._data = self.exec_action(self._data) continue if isinstance(self._data,dict): data_key = data_formatted[target.value] key = self._data[data_key] self.target = {data_key: key} + elif isinstance(self._data, list): + self.target = [d for d in self._data if d == data_formatted[target.value]][0] else: self.target = self._data[data_formatted[target.value]] # Possible enhacement. If run_actions returns false a message line indicating the failure self.run_actions(target.value) - if not target or target == self.cancel_action: # TODO dubious - return self.base_data # return the original list + if target.value == self.cancel_action: # TODO dubious + return self._original_data # return the original list else: return self._data @@ -221,10 +221,9 @@ class ListManager: self.action = choice.value - if not self.action or self.action == self.cancel_action: - return False - else: - return self.exec_action(self._data) + if self.action and self.action != self.cancel_action: + self._data = self.exec_action(self._data) + """ The following methods are expected to be overwritten by the user if the needs of the list are beyond the simple case """ @@ -293,3 +292,5 @@ class ListManager: self._data[origkey] = value elif self.action == str(_('Delete')): del self._data[origkey] + + return self._data diff --git a/archinstall/lib/models/users.py b/archinstall/lib/models/users.py new file mode 100644 index 00000000..6052b73a --- /dev/null +++ b/archinstall/lib/models/users.py @@ -0,0 +1,77 @@ +from dataclasses import dataclass +from typing import Dict, List, Union, Any, TYPE_CHECKING + +if TYPE_CHECKING: + _: Any + + +@dataclass +class User: + username: str + password: str + sudo: bool + + @property + def groups(self) -> List[str]: + # this property should be transferred into a class attr instead + # if it's every going to be used + return [] + + def json(self) -> Dict[str, Any]: + return { + 'username': self.username, + '!password': self.password, + 'sudo': self.sudo + } + + def display(self) -> str: + password = '*' * len(self.password) + return f'{_("Username")}: {self.username:16} {_("Password")}: {password:16} sudo: {str(self.sudo)}' + + @classmethod + def _parse(cls, config_users: List[Dict[str, Any]]) -> List['User']: + users = [] + + for entry in config_users: + username = entry.get('username', None) + password = entry.get('!password', '') + sudo = entry.get('sudo', False) + + if username is None: + continue + + user = User(username, password, sudo) + users.append(user) + + return users + + @classmethod + def _parse_backwards_compatible(cls, config_users: Dict, sudo: bool) -> List['User']: + if len(config_users.keys()) > 0: + username = list(config_users.keys())[0] + password = config_users[username]['!password'] + + if password: + return [User(username, password, sudo)] + + return [] + + @classmethod + def parse_arguments( + cls, + config_users: Union[List[Dict[str, str]], Dict[str, str]], + config_superusers: Union[List[Dict[str, str]], Dict[str, str]] + ) -> List['User']: + users = [] + + # backwards compability + if isinstance(config_users, dict): + users += cls._parse_backwards_compatible(config_users, False) + else: + users += cls._parse(config_users) + + # backwards compability + if isinstance(config_superusers, dict): + users += cls._parse_backwards_compatible(config_superusers, True) + + return users diff --git a/archinstall/lib/output.py b/archinstall/lib/output.py index 07747091..29b73bc4 100644 --- a/archinstall/lib/output.py +++ b/archinstall/lib/output.py @@ -2,11 +2,47 @@ import logging import os import sys from pathlib import Path -from typing import Dict, Union +from typing import Dict, Union, List, Any from .storage import storage +class FormattedOutput: + + @classmethod + def values(cls, o: Any) -> Dict[str, Any]: + if hasattr(o, 'json'): + return o.json() + else: + return o.__dict__ + + @classmethod + def as_table(cls, obj: List[Any]) -> str: + column_width: Dict[str, int] = {} + for o in obj: + for k, v in cls.values(o).items(): + column_width.setdefault(k, 0) + column_width[k] = max([column_width[k], len(str(v)), len(k)]) + + output = '' + for key, width in column_width.items(): + key = key.replace('!', '') + output += key.ljust(width) + ' | ' + + output = output[:-3] + '\n' + output += '-' * len(output) + '\n' + + for o in obj: + for k, v in cls.values(o).items(): + if '!' in k: + v = '*' * len(str(v)) + output += str(v).ljust(column_width[k]) + ' | ' + output = output[:-3] + output += '\n' + + return output + + class Journald: @staticmethod def log(message :str, level :int = logging.DEBUG) -> None: diff --git a/archinstall/lib/user_interaction/__init__.py b/archinstall/lib/user_interaction/__init__.py index b0174d94..8aba4b4d 100644 --- a/archinstall/lib/user_interaction/__init__.py +++ b/archinstall/lib/user_interaction/__init__.py @@ -1,5 +1,5 @@ from .save_conf import save_config -from .manage_users_conf import ask_for_superuser_account, ask_for_additional_users +from .manage_users_conf import ask_for_additional_users from .backwards_compatible_conf import generic_select, generic_multi_select from .locale_conf import select_locale_lang, select_locale_enc from .system_conf import select_kernel, select_harddrives, select_driver, ask_for_bootloader, ask_for_swap diff --git a/archinstall/lib/user_interaction/manage_users_conf.py b/archinstall/lib/user_interaction/manage_users_conf.py index ea909f35..567a2964 100644 --- a/archinstall/lib/user_interaction/manage_users_conf.py +++ b/archinstall/lib/user_interaction/manage_users_conf.py @@ -1,14 +1,12 @@ from __future__ import annotations -import logging import re -from typing import Any, Dict, TYPE_CHECKING, List +from typing import Any, Dict, TYPE_CHECKING, List, Optional +from .utils import get_password from ..menu import Menu from ..menu.list_manager import ListManager -from ..output import log -from ..storage import storage -from .utils import get_password +from ..models.users import User if TYPE_CHECKING: _: Any @@ -19,7 +17,7 @@ class UserList(ListManager): subclass of ListManager for the managing of user accounts """ - def __init__(self, prompt: str, lusers: dict, sudo: bool = None): + def __init__(self, prompt: str, lusers: List[User]): """ param: prompt type: str @@ -27,140 +25,83 @@ class UserList(ListManager): type: Dict param: sudo. boolean to determine if we handle superusers or users. If None handles both types """ - self.sudo = sudo - self.actions = [ + self._actions = [ str(_('Add a user')), str(_('Change password')), str(_('Promote/Demote user')), str(_('Delete User')) ] - super().__init__(prompt, lusers, self.actions, self.actions[0]) - - def reformat(self, data: List) -> Dict: - def format_element(elem :str): - # secret gives away the length of the password - if data[elem].get('!password'): - pwd = '*' * 16 - else: - pwd = '' - if data[elem].get('sudoer'): - super_user = 'Superuser' - else: - super_user = ' ' - return f"{elem:16}: password {pwd:16} {super_user}" + super().__init__(prompt, lusers, self._actions, self._actions[0]) - return {format_element(e): e for e in data} + def reformat(self, data: List[User]) -> Dict[str, User]: + return {e.display(): e for e in data} def action_list(self): - if self.target: - active_user = list(self.target.keys())[0] - else: - active_user = None - sudoer = self.target[active_user].get('sudoer', False) - if self.sudo is None: - return self.actions - if self.sudo and sudoer: - return self.actions - elif self.sudo and not sudoer: - return [self.actions[2]] - elif not self.sudo and sudoer: - return [self.actions[2]] + active_user = self.target if self.target else None + + if active_user is None: + return [self._actions[0]] else: - return self.actions + return self._actions[1:] - def exec_action(self, data: Any): + def exec_action(self, data: List[User]) -> List[User]: if self.target: - active_user = list(self.target.keys())[0] + active_user = self.target else: active_user = None - if self.action == self.actions[0]: # add - new_user = self.add_user() - # no unicity check, if exists will be replaced - data.update(new_user) - elif self.action == self.actions[1]: # change password - data[active_user]['!password'] = get_password( - prompt=str(_('Password for user "{}": ').format(active_user))) - elif self.action == self.actions[2]: # promote/demote - data[active_user]['sudoer'] = not data[active_user]['sudoer'] - elif self.action == self.actions[3]: # delete - del data[active_user] + if self.action == self._actions[0]: # add + new_user = self._add_user() + if new_user is not None: + # in case a user with the same username as an existing user + # was created we'll replace the existing one + data = [d for d in data if d.username != new_user.username] + data += [new_user] + elif self.action == self._actions[1]: # change password + prompt = str(_('Password for user "{}": ').format(active_user.username)) + new_password = get_password(prompt=prompt) + if new_password: + user = next(filter(lambda x: x == active_user, data), 1) + user.password = new_password + elif self.action == self._actions[2]: # promote/demote + user = next(filter(lambda x: x == active_user, data), 1) + user.sudo = False if user.sudo else True + elif self.action == self._actions[3]: # delete + data = [d for d in data if d != active_user] + + return data def _check_for_correct_username(self, username: str) -> bool: if re.match(r'^[a-z_][a-z0-9_-]*\$?$', username) and len(username) <= 32: return True - log("The username you entered is invalid. Try again", level=logging.WARNING, fg='red') return False - def add_user(self): + def _add_user(self) -> Optional[User]: print(_('\nDefine a new user\n')) - prompt = str(_("User Name : ")) + prompt = str(_('Enter username (leave blank to skip): ')) + while True: - userid = input(prompt).strip(' ') - if not userid: - return {} # end - if not self._check_for_correct_username(userid): - pass + username = input(prompt).strip(' ') + if not username: + return None + if not self._check_for_correct_username(username): + prompt = str(_("The username you entered is invalid. Try again")) + '\n' + prompt else: break - if self.sudo: - sudoer = True - elif self.sudo is not None and not self.sudo: - sudoer = False - else: - sudoer = False - sudo_choice = Menu(str(_('Should {} be a superuser (sudoer)?')).format(userid), Menu.yes_no(), - skip=False, - preset_values=Menu.yes() if sudoer else Menu.no(), - default_option=Menu.no()).run() - sudoer = True if sudo_choice == Menu.yes() else False - - password = get_password(prompt=str(_('Password for user "{}": ').format(userid))) - - return {userid: {"!password": password, "sudoer": sudoer}} - - -def manage_users(prompt: str, sudo: bool) -> tuple[dict, dict]: - # TODO Filtering and some kind of simpler code - lusers = {} - if storage['arguments'].get('!superusers', {}): - lusers.update({ - uid: { - '!password': storage['arguments']['!superusers'][uid].get('!password'), - 'sudoer': True - } - for uid in storage['arguments'].get('!superusers', {}) - }) - if storage['arguments'].get('!users', {}): - lusers.update({ - uid: { - '!password': storage['arguments']['!users'][uid].get('!password'), - 'sudoer': False - } - for uid in storage['arguments'].get('!users', {}) - }) - # processing - lusers = UserList(prompt, lusers, sudo).run() - # return data - superusers = { - uid: { - '!password': lusers[uid].get('!password') - } - for uid in lusers if lusers[uid].get('sudoer', False) - } - users = {uid: {'!password': lusers[uid].get('!password')} for uid in lusers if not lusers[uid].get('sudoer', False)} - storage['arguments']['!superusers'] = superusers - storage['arguments']['!users'] = users - return superusers, users - - -def ask_for_superuser_account(prompt: str) -> Dict[str, Dict[str, str]]: - prompt = prompt if prompt else str(_('Define users with sudo privilege, by username: ')) - superusers, dummy = manage_users(prompt, sudo=True) - return superusers - - -def ask_for_additional_users(prompt: str = '') -> Dict[str, Dict[str, str | None]]: - prompt = prompt if prompt else _('Any additional users to install (leave blank for no users): ') - dummy, users = manage_users(prompt, sudo=False) + + password = get_password(prompt=str(_('Password for user "{}": ').format(username))) + + choice = Menu( + str(_('Should "{}" be a superuser (sudo)?')).format(username), Menu.yes_no(), + skip=False, + default_option=Menu.no() + ).run() + + sudo = True if choice.value == Menu.yes() else False + return User(username, password, sudo) + + +def ask_for_additional_users(prompt: str = '', defined_users: List[User] = []) -> List[User]: + prompt = prompt if prompt else _('Enter username (leave blank to skip): ') + users = UserList(prompt, defined_users).run() return users diff --git a/archinstall/lib/user_interaction/network_conf.py b/archinstall/lib/user_interaction/network_conf.py index 25e9d4c6..5154d8b1 100644 --- a/archinstall/lib/user_interaction/network_conf.py +++ b/archinstall/lib/user_interaction/network_conf.py @@ -64,6 +64,8 @@ class ManualNetworkConfig(ListManager): elif self.action == self._action_delete: del data[iface_name] + return data + def _select_iface(self, existing_ifaces: List[str]) -> Optional[Any]: all_ifaces = list_interfaces().values() available = set(all_ifaces) - set(existing_ifaces) diff --git a/archinstall/lib/user_interaction/subvolume_config.py b/archinstall/lib/user_interaction/subvolume_config.py index 7383b13d..af783639 100644 --- a/archinstall/lib/user_interaction/subvolume_config.py +++ b/archinstall/lib/user_interaction/subvolume_config.py @@ -57,6 +57,8 @@ class SubvolumeList(ListManager): data.update(self.target) + return data + class SubvolumeMenu(GeneralMenu): def __init__(self,parameters,action=None): diff --git a/docs/installing/guided.rst b/docs/installing/guided.rst index ac6254f2..569b2d05 100644 --- a/docs/installing/guided.rst +++ b/docs/installing/guided.rst @@ -163,21 +163,20 @@ Options for ``--creds`` "!root-password" : "SecretSanta2022" } -+----------------------+-----------------------------------------------------+--------------------------------------------------------------------------------------+-----------------------------------------------+ -| Key | Values | Description | Required | -| | | | | -+======================+=====================================================+======================================================================================+===============================================+ -| !encryption-password | any | Password to encrypt disk, not encrypted if password not provided | No | -+----------------------+-----------------------------------------------------+--------------------------------------------------------------------------------------+-----------------------------------------------+ -| !root-password | any | The root account password | No | -+----------------------+-----------------------------------------------------+--------------------------------------------------------------------------------------+-----------------------------------------------+ -| !superusers | { "": { "!password": ""}, ..} | List of superuser credentials, see configuration for reference | Yes[1] | -+----------------------+-----------------------------------------------------+--------------------------------------------------------------------------------------+-----------------------------------------------+ -| !users | { "": { "!password": ""}, ..} | List of regular user credentials, see configuration for reference | No | -+----------------------+-----------------------------------------------------+--------------------------------------------------------------------------------------+-----------------------------------------------+ ++----------------------+--------------------------------------------------------+--------------------------------------------------------------------------------------+-----------------------------------------------+ +| Key | Values | Description | Required | ++======================+========================================================+======================================================================================+===============================================+ +| !encryption-password | any | Password to encrypt disk, not encrypted if password not provided | No | ++----------------------+--------------------------------------------------------+--------------------------------------------------------------------------------------+-----------------------------------------------+ +| !root-password | any | The root account password | No | ++----------------------+--------------------------------------------------------+--------------------------------------------------------------------------------------+-----------------------------------------------+ +| !users | { "username": "" | List of regular user credentials, see configuration for reference | No | +| | "!password": "", | | | +| | "sudo": false|true} | | | ++----------------------+--------------------------------------------------------+--------------------------------------------------------------------------------------+-----------------------------------------------+ .. note:: - [1] ``!superusers`` is optional only if ``!root-password`` was set. ``!superusers`` will be enforced otherwise and the minimum amount of superusers required will be set to 1. + [1] ``!users`` is optional only if ``!root-password`` was set. ``!users`` will be enforced otherwise and the minimum amount of users with sudo privileges required will be set to 1. Options for ``--disk_layouts`` ------------------------------ diff --git a/examples/creds-sample.json b/examples/creds-sample.json index 16aeb8cc..0681e16f 100644 --- a/examples/creds-sample.json +++ b/examples/creds-sample.json @@ -1,9 +1,15 @@ { "!root-password": "", - "!users": { - "username": {"!password": ""} - }, - "!superusers": { - "admin": {"!password": ""} - } -} \ No newline at end of file + "!users": [ + { + "username": "", + "!password": "", + "sudo": false + }, + { + "username": "", + "!password": "", + "sudo": true + } + ] +} diff --git a/examples/guided.py b/examples/guided.py index 3b762a8b..19b0d638 100644 --- a/examples/guided.py +++ b/examples/guided.py @@ -72,7 +72,6 @@ def ask_user_questions(): # Ask for a root password (optional, but triggers requirement for super-user if skipped) global_menu.enable('!root-password') - global_menu.enable('!superusers') global_menu.enable('!users') # Ask for archinstall-specific profiles (such as desktop environments etc) @@ -220,13 +219,8 @@ def perform_installation(mountpoint): if archinstall.arguments.get('profile', None): installation.install_profile(archinstall.arguments.get('profile', None)) - if archinstall.arguments.get('!users',{}): - for user, user_info in archinstall.arguments.get('!users', {}).items(): - installation.user_create(user, user_info["!password"], sudo=False) - - if archinstall.arguments.get('!superusers',{}): - for superuser, user_info in archinstall.arguments.get('!superusers', {}).items(): - installation.user_create(superuser, user_info["!password"], sudo=True) + if users := archinstall.arguments.get('!users', None): + installation.create_users(users) if timezone := archinstall.arguments.get('timezone', None): installation.set_timezone(timezone) diff --git a/examples/minimal.py b/examples/minimal.py index d80c4045..8b4c847f 100644 --- a/examples/minimal.py +++ b/examples/minimal.py @@ -1,6 +1,8 @@ import archinstall # Select a harddrive and a disk password +from archinstall import User + archinstall.log("Minimal only supports:") archinstall.log(" * Being installed to a single disk") @@ -28,8 +30,8 @@ def install_on(mountpoint): installation.add_additional_packages(['nano', 'wget', 'git']) installation.install_profile('minimal') - installation.user_create('devel', 'devel') - installation.user_set_pw('root', 'airoot') + user = User('devel', 'devel', False) + installation.create_users(user) # Once this is done, we output some useful information to the user # And the installation is complete. diff --git a/examples/swiss.py b/examples/swiss.py index 2f7d90c4..d0f02dc1 100644 --- a/examples/swiss.py +++ b/examples/swiss.py @@ -219,7 +219,7 @@ class MyMenu(archinstall.GlobalMenu): if self._execution_mode in ('full','lineal'): options_list = ['keyboard-layout', 'mirror-region', 'harddrives', 'disk_layouts', '!encryption-password','swap', 'bootloader', 'hostname', '!root-password', - '!superusers', '!users', 'profile', 'audio', 'kernels', 'packages','additional-repositories','nic', + '!users', 'profile', 'audio', 'kernels', 'packages','additional-repositories','nic', 'timezone', 'ntp'] if archinstall.arguments.get('advanced',False): options_list.extend(['sys-language','sys-encoding']) @@ -229,7 +229,7 @@ class MyMenu(archinstall.GlobalMenu): mandatory_list = ['harddrives'] elif self._execution_mode == 'only_os': options_list = ['keyboard-layout', 'mirror-region','bootloader', 'hostname', - '!root-password', '!superusers', '!users', 'profile', 'audio', 'kernels', + '!root-password', '!users', 'profile', 'audio', 'kernels', 'packages', 'additional-repositories', 'nic', 'timezone', 'ntp'] mandatory_list = ['hostname'] if archinstall.arguments.get('advanced',False): @@ -262,8 +262,12 @@ class MyMenu(archinstall.GlobalMenu): def check(s): return self.option(s).has_selection() + def has_superuser() -> bool: + users = self._menu_options['!users'].current_selection + return any([u.sudo for u in users]) + _, missing = self.mandatory_overview() - if mode in ('full','only_os') and (not check('!root-password') and not check('!superusers')): + if mode in ('full','only_os') and (not check('!root-password') and not has_superuser()): missing += 1 if mode in ('full', 'only_hd') and check('harddrives'): if not self.option('harddrives').is_empty() and not check('disk_layouts'): @@ -420,13 +424,8 @@ def os_setup(installation): if archinstall.arguments.get('profile', None): installation.install_profile(archinstall.arguments.get('profile', None)) - if archinstall.arguments.get('!users',{}): - for user, user_info in archinstall.arguments.get('!users', {}).items(): - installation.user_create(user, user_info["!password"], sudo=False) - - if archinstall.arguments.get('!superusers',{}): - for superuser, user_info in archinstall.arguments.get('!superusers', {}).items(): - installation.user_create(superuser, user_info["!password"], sudo=True) + if users := archinstall.arguments.get('!users', None): + installation.create_users(users) if timezone := archinstall.arguments.get('timezone', None): installation.set_timezone(timezone) diff --git a/profiles/52-54-00-12-34-56.py b/profiles/52-54-00-12-34-56.py index 0a1626d9..3b074629 100644 --- a/profiles/52-54-00-12-34-56.py +++ b/profiles/52-54-00-12-34-56.py @@ -40,7 +40,8 @@ with archinstall.Filesystem(harddrive) as fs: installation.add_additional_packages(__packages__) installation.install_profile('awesome') - installation.user_create('devel', 'devel') + user = User('devel', 'devel', False) + installation.create_users(user) installation.user_set_pw('root', 'toor') print(f'Submitting {archinstall.__version__}: success') diff --git a/schema.json b/schema.json index e6f73c6c..9269e8e8 100644 --- a/schema.json +++ b/schema.json @@ -167,7 +167,15 @@ }, { "required": [ - "!superusers" + "!users": { + "description": "User account", + "type": "object", + "properties": { + "username": "string", + "!password": "string", + "sudo": "boolean" + } + } ] } ] -- cgit v1.2.3-70-g09d2 From 03c1944dae637f5fc7908c7d61ea5002ebe99aae Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Fri, 27 May 2022 20:38:25 +0200 Subject: Removed nemo configuration from awesome profile, after #1247 --- profiles/awesome.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'profiles') diff --git a/profiles/awesome.py b/profiles/awesome.py index 4593261b..11c8de3b 100644 --- a/profiles/awesome.py +++ b/profiles/awesome.py @@ -48,8 +48,4 @@ if __name__ == 'awesome': with open(f"{archinstall.storage['installation_session'].target}/etc/xdg/awesome/rc.lua", 'w') as fh: fh.write(awesome_lua) - # TODO: Configure the right-click-menu to contain the above packages that were installed. (as a user config) - - # Remove some interfering nemo settings - archinstall.storage['installation_session'].arch_chroot("gsettings set org.nemo.desktop show-desktop-icons false") - archinstall.storage['installation_session'].arch_chroot("xdg-mime default nemo.desktop inode/directory application/x-gnome-saved-search") + # TODO: Configure the right-click-menu to contain the above packages that were installed. (as a user config) \ No newline at end of file -- cgit v1.2.3-70-g09d2